// Copyright(c) 2015-2020, NVIDIA CORPORATION. All rights reserved. // // 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. #include "VulkanHppGenerator.hpp" #include "XMLHelper.hpp" #include #include #include #include #include #include #include using namespace std::literals; namespace { template bool containsByName( std::vector const & values, std::string const & name ); template bool containsByNameOrAlias( std::map const & values, std::string const & name ); std::vector> filterNumbers( std::vector const & names ); template typename std::vector::const_iterator findByName( std::vector const & values, std::string const & name ); template typename std::vector::iterator findByName( std::vector & values, std::string const & name ); template typename std::map::const_iterator findByNameOrAlias( std::map const & values, std::string const & name ); template typename std::map::iterator findByNameOrAlias( std::map & values, std::string const & name ); template typename std::vector::const_iterator findByNameOrAlias( std::vector const & values, std::string const & name ); template typename std::vector::const_iterator findByType( std::vector const & values, std::string const & type ); std::string generateCArraySizes( std::vector const & sizes ); std::string generateList( std::vector const & elements, std::string const & prefix, std::string const & separator ); std::string generateNoDiscard( bool returnsSomething, bool multiSuccessCodes, bool multiErrorCodes ); std::string generateStandardArray( std::string const & type, std::vector const & sizes ); bool isUpperCase( std::string const & name ); VulkanHppGenerator::MacroData parseMacro( std::vector const & completeMacro ); std::string startLowerCase( std::string const & input ); std::string startUpperCase( std::string const & input ); std::vector tokenizeAny( std::string const & tokenString, std::string const & separators ); } // namespace // Some types come in as non-const pointers, but are meant as const-pointers const std::set specialPointerTypes = { "Display", "IDirectFB", "wl_display", "xcb_connection_t", "_screen_window", "VkExportMetalObjectsInfoEXT" }; // // VulkanHppGenerator public interface // VulkanHppGenerator::VulkanHppGenerator( tinyxml2::XMLDocument const & document, std::string const & api ) : m_api( api ) { // insert the default "handle" without class (for createInstance, and such) m_handles.insert( { "", {} } ); // read the document and check its correctness const int line = document.GetLineNum(); std::vector elements = getChildElements( &document ); checkElements( line, elements, { { "registry", true } } ); readRegistry( elements[0] ); filterLenMembers(); checkCorrectness(); handleRemovals(); // add the commands to the respective handles // some "FlagBits" enums are not specified, but needed for our "Flags" handling -> add them here for ( auto & feature : m_features ) { forEachRequiredCommand( feature.requireData, [this]( NameLine const & command, auto const & commandData ) { addCommandToHandle( { command.name, commandData.second } ); } ); addMissingFlagBits( feature.requireData, feature.name ); } for ( auto & extension : m_extensions ) { forEachRequiredCommand( extension.requireData, [this]( NameLine const & command, auto const & commandData ) { addCommandToHandle( { command.name, commandData.second } ); } ); addMissingFlagBits( extension.requireData, extension.name ); } m_definesPartition = partitionDefines( m_defines ); } void VulkanHppGenerator::distributeSecondLevelCommands() { // distribute commands from instance/device to second-level handles, like Queue, Event,... for RAII handles assert( !m_handles.empty() && m_handles.begin()->first.empty() ); // the first "handle" is the empty one for ( auto handleIt = std::next( m_handles.begin() ); handleIt != m_handles.end(); ++handleIt ) { for ( auto command = handleIt->second.commands.begin(); command != handleIt->second.commands.end(); ) { bool foundCommand = false; if ( !m_RAIISpecialFunctions.contains( *command ) ) { auto commandIt = findByNameOrAlias( m_commands, *command ); assert( commandIt != m_commands.end() ); assert( commandIt->second.params.front().type.type == handleIt->first ); if ( ( 1 < commandIt->second.params.size() ) && ( isHandleType( commandIt->second.params[1].type.type ) ) && !commandIt->second.params[1].optional ) { auto secondLevelHandleIt = m_handles.find( commandIt->second.params[1].type.type ); assert( secondLevelHandleIt != m_handles.end() ); // filter out functions that seem to fit due to taking handles as first and second argument, but the first argument is not the // type to create the second one, and so it's unknown to the raii handle! assert( !secondLevelHandleIt->second.constructorIts.empty() ); if ( ( *secondLevelHandleIt->second.constructorIts.begin() )->second.handle == handleIt->first ) { assert( std::ranges::none_of( secondLevelHandleIt->second.constructorIts, [handleIt]( auto const & constructorIt ) { return constructorIt->second.handle != handleIt->first; } ) ); secondLevelHandleIt->second.secondLevelCommands.insert( *command ); command = handleIt->second.commands.erase( command ); foundCommand = true; } } } if ( !foundCommand ) { ++command; } } } } void VulkanHppGenerator::generateCppmFile() const { generateFileFromTemplate( m_api + ".cppm", "CppmTemplate.hpp", { { "api", m_api }, { "hashSpecializations", generateCppModuleHashSpecializations() }, { "licenseHeader", m_vulkanLicenseHeader }, { "raiiUsings", generateCppModuleRaiiUsings() }, { "usings", generateCppModuleUsings() }, { "pfnCommands", generateCppModuleCommands() } } ); } void VulkanHppGenerator::generateEnumsHppFile() const { generateFileFromTemplate( m_api + "_enums.hpp", "EnumsHppTemplate.hpp", { { "enums", generateEnums() }, { "Flags", readSnippet( "Flags.hpp" ) }, { "licenseHeader", m_vulkanLicenseHeader }, { "objectTypeToDebugReportObjectType", generateObjectTypeToDebugReportObjectType() } } ); } void VulkanHppGenerator::generateExtensionInspectionFile() const { generateFileFromTemplate( m_api + "_extension_inspection.hpp", "ExtensionInspectionHppTemplate.hpp", { { "api", m_api }, { "deprecatedExtensions", generateReplacedExtensionsList( []( ExtensionData const & extension ) { return extension.isDeprecated; }, []( ExtensionData const & extension ) { return extension.deprecatedBy; } ) }, { "deviceExtensions", generateExtensionsList( "device" ) }, { "deviceTest", generateExtensionTypeTest( "device" ) }, { "deprecatedBy", generateExtensionReplacedBy( []( ExtensionData const & extension ) { return extension.isDeprecated; }, []( ExtensionData const & extension ) { return extension.deprecatedBy; } ) }, { "deprecatedTest", generateExtensionReplacedTest( []( ExtensionData const & extension ) { return extension.isDeprecated; } ) }, { "extensionDependencies", generateExtensionDependencies() }, { "instanceExtensions", generateExtensionsList( "instance" ) }, { "instanceTest", generateExtensionTypeTest( "instance" ) }, { "licenseHeader", m_vulkanLicenseHeader }, { "obsoletedBy", generateExtensionReplacedBy( []( ExtensionData const & extension ) { return !extension.obsoletedBy.empty(); }, []( ExtensionData const & extension ) { return extension.obsoletedBy; } ) }, { "obsoletedExtensions", generateReplacedExtensionsList( []( ExtensionData const & extension ) { return !extension.obsoletedBy.empty(); }, []( ExtensionData const & extension ) { return extension.obsoletedBy; } ) }, { "obsoletedTest", generateExtensionReplacedTest( []( ExtensionData const & extension ) { return !extension.obsoletedBy.empty(); } ) }, { "promotedExtensions", generateReplacedExtensionsList( []( ExtensionData const & extension ) { return !extension.promotedTo.empty(); }, []( ExtensionData const & extension ) { return extension.promotedTo; } ) }, { "promotedTest", generateExtensionReplacedTest( []( ExtensionData const & extension ) { return !extension.promotedTo.empty(); } ) }, { "promotedTo", generateExtensionReplacedBy( []( ExtensionData const & extension ) { return !extension.promotedTo.empty(); }, []( ExtensionData const & extension ) { return extension.promotedTo; } ) }, { "versions", std::accumulate( std::next( m_features.begin() ), m_features.end(), "\"" + m_features[0].name + "\"", []( std::string const & acc, auto const & feature ) { return acc + ", \"" + feature.name + "\""; } ) }, { "voidExtension", ( m_api == "vulkan" ) ? "" : "(void)extension;" } } ); } void VulkanHppGenerator::generateFormatTraitsHppFile() const { generateFileFromTemplate( m_api + "_format_traits.hpp", "FormatTraitsHppTemplate.hpp", { { "api", m_api }, { "formatTraits", generateFormatTraits() }, { "licenseHeader", m_vulkanLicenseHeader } } ); } void VulkanHppGenerator::generateFuncsHppFile() const { generateFileFromTemplate( m_api + "_funcs.hpp", "FuncsHppTemplate.hpp", { { "commandDefinitions", generateCommandDefinitions() }, { "licenseHeader", m_vulkanLicenseHeader } } ); } void VulkanHppGenerator::generateHandlesHppFile() const { generateFileFromTemplate( m_api + "_handles.hpp", "HandlesHppTemplate.hpp", { { "funcPointerReturns", generateFuncPointerReturns() }, { "handles", generateHandles() }, { "handleForwardDeclarations", generateHandleForwardDeclarations() }, { "licenseHeader", m_vulkanLicenseHeader }, { "structForwardDeclarations", generateStructForwardDeclarations() }, { "uniqueHandles", generateUniqueHandles() } } ); } void VulkanHppGenerator::generateHashHppFile() const { generateFileFromTemplate( m_api + "_hash.hpp", "HashHppTemplate.hpp", { { "api", m_api }, { "handleHashStructures", generateHandleHashStructures() }, { "licenseHeader", m_vulkanLicenseHeader }, { "structHashStructures", generateStructHashStructures() } } ); } void VulkanHppGenerator::generateHppFile() const { generateFileFromTemplate( m_api + ".hpp", "HppTemplate.hpp", { { "api", m_api }, { "ArrayProxy", readSnippet( "ArrayProxy.hpp" ) }, { "ArrayProxyNoTemporaries", readSnippet( "ArrayProxyNoTemporaries.hpp" ) }, { "ArrayWrapper1D", readSnippet( "ArrayWrapper1D.hpp" ) }, { "ArrayWrapper2D", readSnippet( "ArrayWrapper2D.hpp" ) }, { "baseTypes", generateBaseTypes() }, { "constexprDefines", generateConstexprDefines() }, { "defines", readSnippet( "defines.hpp" ) }, { "DispatchLoaderBase", readSnippet( "DispatchLoaderBase.hpp" ) }, { "DispatchLoaderDynamic", generateDispatchLoaderDynamic() }, { "DispatchLoaderStatic", generateDispatchLoaderStatic() }, { "DynamicLoader", readSnippet( "DynamicLoader.hpp" ) }, { "Exceptions", readSnippet( "Exceptions.hpp" ) }, { "Exchange", readSnippet( "Exchange.hpp" ) }, { "headerVersion", m_version }, { "includes", replaceWithMap( readSnippet( "includes.hpp" ), { { "vulkan_h", ( m_api == "vulkansc" ) ? "vulkan_sc_core.h" : ( m_api + ".h" ) } } ) }, { "IsDispatchedList", generateIsDispatchedList() }, { "licenseHeader", m_vulkanLicenseHeader }, { "ObjectDestroy", readSnippet( "ObjectDestroy.hpp" ) }, { "ObjectFree", readSnippet( "ObjectFree.hpp" ) }, { "ObjectRelease", readSnippet( "ObjectRelease.hpp" ) }, { "Optional", readSnippet( "Optional.hpp" ) }, { "PoolFree", readSnippet( "PoolFree.hpp" ) }, { "resultChecks", readSnippet( "resultChecks.hpp" ) }, { "resultExceptions", generateResultExceptions() }, { "structExtendsStructs", generateStructExtendsStructs() }, { "ResultValue", readSnippet( "ResultValue.hpp" ) }, { "StridedArrayProxy", readSnippet( "StridedArrayProxy.hpp" ) }, { "StructureChain", readSnippet( "StructureChain.hpp" ) }, { "throwResultException", generateThrowResultException() }, { "UniqueHandle", readSnippet( "UniqueHandle.hpp" ) } } ); } void VulkanHppGenerator::generateMacrosFile() const { generateFileFromTemplate( m_api + "_hpp_macros.hpp", "MacrosHppTemplate.hpp", { { "licenseHeader", m_vulkanLicenseHeader }, { "vulkan_hpp", m_api + ".hpp" }, { "vulkan_64_bit_ptr_defines", m_defines.at( "VK_USE_64_BIT_PTR_DEFINES" ).possibleDefinition } } ); } void VulkanHppGenerator::generateRAIIHppFile() const { generateFileFromTemplate( m_api + "_raii.hpp", "RAIIHppTemplate.hpp", { { "api", m_api }, { "licenseHeader", m_vulkanLicenseHeader }, { "RAIICommandDefinitions", generateRAIICommandDefinitions() }, { "RAIIDispatchers", generateRAIIDispatchers() }, { "RAIIHandles", generateRAIIHandles() } } ); } void VulkanHppGenerator::generateSharedHppFile() const { generateFileFromTemplate( m_api + "_shared.hpp", "SharedHppTemplate.hpp", { { "api", m_api }, { "licenseHeader", m_vulkanLicenseHeader }, { "sharedHandles", generateSharedHandles() }, { "sharedHandlesNoDestroy", generateSharedHandlesNoDestroy() } } ); } void VulkanHppGenerator::generateStaticAssertionsHppFile() const { generateFileFromTemplate( m_api + "_static_assertions.hpp", "StaticAssertionsHppTemplate.hpp", { { "api", m_api }, { "licenseHeader", m_vulkanLicenseHeader }, { "staticAssertions", generateStaticAssertions() } } ); } void VulkanHppGenerator::generateStructsHppFile() const { generateFileFromTemplate( m_api + "_structs.hpp", "StructsHppTemplate.hpp", { { "licenseHeader", m_vulkanLicenseHeader }, { "structs", generateStructs() } } ); } void VulkanHppGenerator::generateToStringHppFile() const { generateFileFromTemplate( m_api + "_to_string.hpp", "ToStringHppTemplate.hpp", { { "api", m_api }, { "bitmasksToString", generateBitmasksToString() }, { "enumsToString", generateEnumsToString() }, { "licenseHeader", m_vulkanLicenseHeader } } ); } void VulkanHppGenerator::prepareRAIIHandles() { // filter out functions that are not usefull on this level of abstraction (like vkGetInstanceProcAddr) // and all the destruction functions, as they are used differently assert( m_handles.begin()->first.empty() ); for ( auto handleIt = std::next( m_handles.begin() ); handleIt != m_handles.end(); ++handleIt ) { handleIt->second.destructorIt = determineRAIIHandleDestructor( handleIt->first ); if ( handleIt->second.destructorIt != m_commands.end() ) { m_RAIISpecialFunctions.insert( handleIt->second.destructorIt->first ); } handleIt->second.constructorIts = determineRAIIHandleConstructors( handleIt->first, handleIt->second.destructorIt ); } } // // VulkanHppGenerator private interface // void VulkanHppGenerator::addCommand( std::string const & name, CommandData && commandData ) { checkForError( !commandData.params.empty(), commandData.xmlLine, "command <" + name + "> with no params" ); auto [commandIt, inserted] = m_commands.insert( { name, std::move( commandData ) } ); checkForError( inserted, commandData.xmlLine, "already encountered command <" + name + ">" ); // find the handle this command is going to be associated to auto handleIt = m_handles.find( commandIt->second.params[0].type.type ); if ( handleIt == m_handles.end() ) { handleIt = m_handles.begin(); assert( handleIt->first == "" ); } commandIt->second.handle = handleIt->first; } void VulkanHppGenerator::addCommandToHandle( std::pair const & commandData ) { auto handleIt = m_handles.find( commandData.second.handle ); assert( handleIt != m_handles.end() ); if ( !handleIt->second.commands.contains( commandData.first ) ) { handleIt->second.commands.insert( commandData.first ); registerDeleter( commandData.first, commandData.second ); } } void VulkanHppGenerator::addMissingFlagBits( std::vector & requireData, std::string const & requiredBy ) { for ( auto & require : requireData ) { std::vector newTypes; for ( auto const & type : require.types ) { auto bitmaskIt = m_bitmasks.find( type.name ); if ( bitmaskIt != m_bitmasks.end() ) { if ( bitmaskIt->second.require.empty() ) { // generate the flagBits enum name out of the bitmask name: VkFooFlagsXXX -> VkFooFlagBitsXXX const size_t pos = bitmaskIt->first.find( "Flags" ); assert( pos != std::string::npos ); std::string flagBits = bitmaskIt->first.substr( 0, pos + 4 ) + "Bit" + bitmaskIt->first.substr( pos + 4 ); assert( flagBits.find( "Flags" ) == std::string::npos ); bitmaskIt->second.require = flagBits; // some flagsBits are specified but never listed as required for any flags! // so, even if this bitmask has no enum listed as required, it might still already exist in the enums list auto enumIt = m_enums.find( flagBits ); if ( enumIt == m_enums.end() ) { m_enums.insert( { flagBits, EnumData{ .isBitmask = true, .xmlLine = 0 } } ); assert( !m_types.contains( flagBits ) ); m_types.insert( { flagBits, TypeData{ TypeCategory::Bitmask, { requiredBy }, 0 } } ); } else { assert( m_types.contains( flagBits ) ); enumIt->second.isBitmask = true; } } if ( std::ranges::none_of( require.types, [bitmaskIt]( auto const & requireType ) { return requireType.name == bitmaskIt->second.require; } ) ) { // this bitmask requires a flags type that is not listed in here, so add it newTypes.push_back( { bitmaskIt->second.require, bitmaskIt->second.xmlLine } ); } } } // add all the newly created flagBits types to the require list as if they had been part of the vk.xml! require.types.insert( require.types.end(), newTypes.begin(), newTypes.end() ); } } std::string VulkanHppGenerator::addTitleAndProtection( std::string const & title, std::string const & strIf, std::string const & strElse ) const { std::string str; if ( !strIf.empty() ) { const auto [enter, leave] = generateProtection( getProtectFromTitle( title ) ); str = "\n" + enter + " //=== " + title + " ===\n" + strIf; if ( !enter.empty() && !strElse.empty() ) { str += "#else \n" + strElse; } str += leave; } return str; } bool VulkanHppGenerator::allVectorSizesSupported( std::vector const & params, std::map const & vectorParams ) const { return std::ranges::all_of( vectorParams, [¶ms]( auto const & vpi ) { static const std::set sizeTypes = { "size_t", "uint32_t", "VkDeviceSize", "VkSampleCountFlagBits" }; return sizeTypes.contains( params[vpi.second.lenParam].type.type ); } ); } void VulkanHppGenerator::appendCppModuleCommands( std::vector const & requireData, std::set & listedCommands, std::string const & title, std::string & commandMembers ) const { std::string members; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & ) { if ( listedCommands.insert( command.name ).second ) { members += "using ::PFN_" + command.name + ";\n"; } } ); commandMembers += addTitleAndProtection( title, members ); } void VulkanHppGenerator::checkAttributes( int line, std::map const & attributes, std::map> const & required, std::map> const & optional ) const { ::checkAttributes( "VulkanHppGenerator", line, attributes, required, optional ); } void VulkanHppGenerator::checkBitmaskCorrectness() const { for ( auto const & bitmask : m_bitmasks ) { // check that a bitmask is required somewhere // I think, it's not forbidden to not reference a bitmask, but it would probably be not intended? auto typeIt = m_types.find( bitmask.first ); assert( typeIt != m_types.end() ); checkForError( !typeIt->second.requiredBy.empty(), bitmask.second.xmlLine, "bitmask <" + bitmask.first + "> not required in any feature or extension" ); // check that the requirement is an enum if ( !bitmask.second.require.empty() ) { auto requireTypeIt = m_types.find( bitmask.second.require ); checkForError( requireTypeIt != m_types.end(), bitmask.second.xmlLine, "bitmask <" + bitmask.first + "> requires unknown type <" + bitmask.second.require + ">" ); checkForError( requireTypeIt->second.category == TypeCategory::Enum, bitmask.second.xmlLine, "bitmask <" + bitmask.first + "> requires non-enum type <" + bitmask.second.require + ">" ); assert( m_enums.contains( bitmask.second.require ) ); checkForError( !requireTypeIt->second.requiredBy.empty(), bitmask.second.xmlLine, "bitmask <" + bitmask.first + "> requires <" + bitmask.second.require + "> which is not required by any feature or extension!" ); } } } void VulkanHppGenerator::checkCommandCorrectness() const { // prepare command checks by gathering all result codes (including aliases and not supported ones!) into one set of resultCodes std::set resultCodes = gatherResultCodes(); // command checks for ( auto const & command : m_commands ) { // check that a command is referenced somewhere // I think, it's not forbidden to not reference a function, but it would probably be not intended? checkForError( !command.second.requiredBy.empty(), command.second.xmlLine, "command <" + command.first + "> not required in any feature or extension" ); // check for unknown error or succes codes for ( auto const & ec : command.second.errorCodes ) { checkForError( resultCodes.contains( ec ), command.second.xmlLine, "command uses unknown error code <" + ec + ">" ); } for ( auto const & sc : command.second.successCodes ) { checkForError( resultCodes.contains( sc ), command.second.xmlLine, "command uses unknown success code <" + sc + ">" ); } // check that functions returning a VkResult specify successcodes if ( ( command.second.returnType.type == "VkResult" ) && command.second.successCodes.empty() ) { // emit an error if this function is required in at least one supported feature or extension // disabled or not supported features/extensions are still listed in requiredBy, but not in m_features/m_extensions bool functionUsed = false; for ( auto const & require : command.second.requiredBy ) { functionUsed |= isSupportedFeature( require ) || isSupportedExtension( require ); } if ( functionUsed ) { checkForError( false, command.second.xmlLine, "missing successcodes on command <" + command.first + "> returning VkResult!" ); } } // check that all parameter types as well as the return type are known types for ( auto const & p : command.second.params ) { checkForError( m_types.contains( p.type.type ), p.xmlLine, "comand uses parameter of unknown type <" + p.type.type + ">" ); } checkForError( m_types.contains( command.second.returnType.type ), command.second.xmlLine, "command uses unknown return type <" + command.second.returnType.type + ">" ); } } void VulkanHppGenerator::checkCorrectness() const { checkForError( !m_vulkanLicenseHeader.empty(), -1, "missing license header" ); checkBitmaskCorrectness(); checkCommandCorrectness(); checkDefineCorrectness(); checkEnumCorrectness(); checkExtensionCorrectness(); checkFuncPointerCorrectness(); checkHandleCorrectness(); checkRequireCorrectness(); checkSpirVCapabilityCorrectness(); checkStructCorrectness(); checkSyncAccessCorrectness(); checkSyncStageCorrectness(); } void VulkanHppGenerator::checkDefineCorrectness() const { // check that any requirements of a define is known for ( auto const & d : m_defines ) { checkForError( d.second.require.empty() || m_types.contains( d.second.require ), d.second.xmlLine, "define <" + d.first + "> uses unknown require <" + d.second.require + ">" ); } } void VulkanHppGenerator::checkElements( int line, std::vector const & elements, std::map const & required, std::set const & optional ) const { ::checkElements( "VulkanHppGenerator", line, elements, required, optional ); } void VulkanHppGenerator::checkEnumCorrectness() const { for ( auto const & e : m_enums ) { // check that a bitmask is required somewhere // some bitmasks are never required, so make this a warning only auto typeIt = m_types.find( e.first ); assert( typeIt != m_types.end() ); checkForWarning( !typeIt->second.requiredBy.empty(), e.second.xmlLine, "enum <" + e.first + "> not required in any feature or extension" ); if ( e.second.isBitmask ) { if ( std::ranges::any_of( e.second.values, []( auto const & v ) { return v.supported; } ) ) { // check that any enum of a bitmask with supported values is listed as "require" or "bitvalues" for a bitmask auto bitmaskIt = std::ranges::find_if( m_bitmasks, [&e]( auto const & bitmask ) { return bitmask.second.require == e.first; } ); checkForError( bitmaskIt != m_bitmasks.end(), e.second.xmlLine, "enum <" + e.first + "> is not listed as an requires or bitvalues for any bitmask in the types section" ); // check that bitwidth of the enum and type of the corresponding bitmask are equal checkForError( ( e.second.bitwidth != "64" ) || ( bitmaskIt->second.type == "VkFlags64" ), e.second.xmlLine, "enum <" + e.first + "> is marked with bitwidth <64> but corresponding bitmask <" + bitmaskIt->first + "> is not of type " ); } } } // special check for VkFormat if ( !m_formats.empty() ) { auto enumIt = m_enums.find( "VkFormat" ); assert( enumIt != m_enums.end() ); assert( enumIt->second.values.front().name == "VK_FORMAT_UNDEFINED" ); for ( auto enumValueIt = std::next( enumIt->second.values.begin() ); enumValueIt != enumIt->second.values.end(); ++enumValueIt ) { checkForError( !enumValueIt->supported || m_formats.contains( enumValueIt->name ), enumValueIt->xmlLine, "missing format specification for <" + enumValueIt->name + ">" ); } } } bool VulkanHppGenerator::checkEquivalentSingularConstructor( std::vector::const_iterator> const & constructorIts, std::map::const_iterator constructorIt, std::vector::const_iterator lenIt ) const { // check, if there is no singular constructor with the very same arguments as this array constructor // (besides the size, of course) auto isEquivalentSingularConstructor = [constructorIt, lenIt]( std::map::const_iterator it ) { if ( it->second.params.size() + 1 != constructorIt->second.params.size() ) { return false; } const size_t lenIdx = std::distance( constructorIt->second.params.begin(), lenIt ); for ( size_t i = 0, j = 0; i < it->second.params.size(); ++i, ++j ) { assert( j < constructorIt->second.params.size() ); if ( j == lenIdx ) { ++j; } if ( it->second.params[i].type.type != constructorIt->second.params[j].type.type ) { return false; } } return true; }; return ( std::ranges::any_of( constructorIts, isEquivalentSingularConstructor ) ); } void VulkanHppGenerator::checkExtensionCorrectness() const { for ( auto const & extension : m_extensions ) { // check for existence of any depends, deprecation, obsoletion, or promotion for ( auto const & dep : extension.depends ) { checkForError( isFeature( dep.first ), extension.xmlLine, "extension <" + extension.name + "> lists an unknown feature dependency <" + dep.first + ">" ); for ( auto depsIt = dep.second.begin(); depsIt != dep.second.end(); ++depsIt ) { for ( auto const & d : *depsIt ) { checkForError( isExtension( d ), extension.xmlLine, "extension <" + extension.name + "> lists an unknown extension dependency <" + d + ">" ); } checkForError( std::none_of( std::next( depsIt ), dep.second.end(), [&depsIt]( auto const & d ) { return *depsIt == d; } ), extension.xmlLine, "extension <" + extension.name + "> lists multiple identical dependencies" ); } } checkForError( extension.deprecatedBy.empty() || isFeature( extension.deprecatedBy ) || isExtension( extension.deprecatedBy ), extension.xmlLine, "extension <" + extension.name + "> is deprecated by unknown extension/version <" + extension.deprecatedBy + ">" ); checkForError( extension.obsoletedBy.empty() || isFeature( extension.obsoletedBy ) || isExtension( extension.obsoletedBy ), extension.xmlLine, "extension <" + extension.name + "> is obsoleted by unknown extension/version <" + extension.obsoletedBy + ">" ); checkForError( extension.promotedTo.empty() || isFeature( extension.promotedTo ) || isExtension( extension.promotedTo ), extension.xmlLine, "extension <" + extension.name + "> is promoted to unknown extension/version <" + extension.promotedTo + ">" ); // check for existence of any requirement for ( auto const & require : extension.requireData ) { std::vector requireDepends = tokenizeAny( require.depends, ",+()" ); for ( auto const & dep : requireDepends ) { checkForError( isFeature( dep ) || isExtension( dep ), require.xmlLine, "extension <" + extension.name + "> lists an unknown depends <" + dep + ">" ); } } } } inline void VulkanHppGenerator::checkForError( bool condition, int line, std::string const & message ) const { ::checkForError( "VulkanHppGenerator", condition, line, message ); } void VulkanHppGenerator::checkForWarning( bool condition, int line, std::string const & message ) const { ::checkForWarning( "VulkanHppGenerator", condition, line, message ); } void VulkanHppGenerator::checkFuncPointerCorrectness() const { for ( auto const & funcPointer : m_funcPointers ) { checkForError( funcPointer.second.require.empty() || m_types.contains( funcPointer.second.require ), funcPointer.second.xmlLine, "funcpointer requires unknown <" + funcPointer.second.require + ">" ); for ( auto const & param : funcPointer.second.params ) { checkForError( m_types.contains( param.type.type ), param.xmlLine, "funcpointer param of unknown type <" + param.type.type + ">" ); } } } void VulkanHppGenerator::checkHandleCorrectness() const { // prepare handle checks by getting the VkObjectType enum auto objectTypeIt = m_enums.find( "VkObjectType" ); assert( objectTypeIt != m_enums.end() ); // handle checks assert( m_handles.begin()->first.empty() ); for ( auto handleIt = std::next( m_handles.begin() ); handleIt != m_handles.end(); ++handleIt ) { assert( !handleIt->first.empty() ); // check the existence of the parent checkForError( m_handles.contains( handleIt->second.parent ), handleIt->second.xmlLine, "handle <" + handleIt->first + "> with unknown parent <" + handleIt->second.parent + ">" ); // check existence of objTypeEnum used with this handle type checkForError( !handleIt->second.objTypeEnum.empty(), handleIt->second.xmlLine, "handle <" + handleIt->first + "> missing required \"objtypeenum\" attribute" ); checkForError( !isTypeUsed( handleIt->first ) || containsName( objectTypeIt->second.values, handleIt->second.objTypeEnum ), handleIt->second.xmlLine, "handle <" + handleIt->first + "> specifies unknown \"objtypeenum\" <" + handleIt->second.objTypeEnum + ">" ); } // check that all specified objectType values are used with a handle type for ( auto const & objectTypeValue : objectTypeIt->second.values ) { checkForError( ( objectTypeValue.name == "VK_OBJECT_TYPE_UNKNOWN" ) || std::ranges::any_of( m_handles, [&objectTypeValue]( std::pair const & hd ) { return hd.second.objTypeEnum == objectTypeValue.name; } ), objectTypeValue.xmlLine, "VkObjectType value <" + objectTypeValue.name + "> not specified as \"objtypeenum\" for any handle" ); } } void VulkanHppGenerator::checkRequireCorrectness() const { // checks by features and extensions for ( auto & feature : m_features ) { checkRequireCorrectness( feature.requireData, "feature", feature.name ); } for ( auto & extension : m_extensions ) { checkRequireCorrectness( extension.requireData, "extension", extension.name ); } } void VulkanHppGenerator::checkRequireCorrectness( std::vector const & requireData, std::string const & section, std::string const & name ) const { for ( auto const & require : requireData ) { checkRequireDependenciesCorrectness( require, section, name ); checkRequireTypesCorrectness( require ); } } void VulkanHppGenerator::checkRequireDependenciesCorrectness( RequireData const & require, std::string const & section, std::string const & name ) const { std::vector dependencies = tokenizeAny( require.depends, ",+()" ); for ( auto const & depends : dependencies ) { size_t separatorPos = depends.find( "::" ); if ( separatorPos == std::string::npos ) { checkForError( isFeature( depends ) || isExtension( depends ), require.xmlLine, section + " <" + name + "> depends on unknown extension or feature <" + depends + ">" ); } else { std::string structure = depends.substr( 0, separatorPos ); std::string member = depends.substr( separatorPos + 2 ); auto structIt = m_structs.find( structure ); checkForError( structIt != m_structs.end(), require.xmlLine, section + " <" + name + "> requires member of an unknown struct <" + structure + ">" ); checkForError( std::ranges::any_of( structIt->second.members, [&member]( auto const & md ) { return md.name == member; } ), require.xmlLine, section + " <" + name + "> requires unknown member <" + member + "> as part of the struct <" + structure + ">" ); } } } void VulkanHppGenerator::checkRequireTypesCorrectness( RequireData const & require ) const { for ( auto const & type : require.types ) { auto typeIt = m_types.find( type.name ); assert( typeIt != m_types.end() ); // every required type should be listed in the corresponding map switch ( typeIt->second.category ) { case TypeCategory::Bitmask: checkForError( findByNameOrAlias( m_bitmasks, type.name ) != m_bitmasks.end(), typeIt->second.xmlLine, "required bitmask type <" + type.name + "> is not listed as bitmask" ); break; case TypeCategory::BaseType: checkForError( m_baseTypes.contains( type.name ), typeIt->second.xmlLine, "required base type <" + type.name + "> is not listed as a base type" ); break; case TypeCategory::Constant: checkForError( m_constants.contains( type.name ), typeIt->second.xmlLine, "required constant <" + type.name + "> is not listed as a constant" ); break; case TypeCategory::Define: checkForError( m_defines.contains( type.name ), typeIt->second.xmlLine, "required define <" + type.name + "> is not listed as a define" ); break; case TypeCategory::Enum: checkForError( findByNameOrAlias( m_enums, type.name ) != m_enums.end(), typeIt->second.xmlLine, "required enum type <" + type.name + "> is not listed as an enum" ); break; case TypeCategory::ExternalType: checkForError( m_externalTypes.contains( type.name ), typeIt->second.xmlLine, "required external type <" + type.name + "> is not listed as an external type" ); break; case TypeCategory::FuncPointer: checkForError( m_funcPointers.contains( type.name ), typeIt->second.xmlLine, "required funcpointer <" + type.name + "> is not listed as a funcpointer" ); break; case TypeCategory::Handle: checkForError( findByNameOrAlias( m_handles, type.name ) != m_handles.end(), typeIt->second.xmlLine, "required handle type <" + type.name + "> is not listed as a handle" ); break; case TypeCategory::Include: checkForError( m_includes.contains( type.name ), typeIt->second.xmlLine, "required include <" + type.name + "> is not listed as an include" ); break; case TypeCategory::Struct: case TypeCategory::Union: checkForError( findByNameOrAlias( m_structs, type.name ) != m_structs.end(), typeIt->second.xmlLine, "required struct type <" + type.name + "> is not listed as a struct" ); break; case TypeCategory::Unknown: break; default : assert( false ); break; } } } void VulkanHppGenerator::checkSpirVCapabilityCorrectness() const { for ( auto const & capability : m_spirVCapabilities ) { for ( auto const & enable : capability.second.structs ) { assert( !enable.second.empty() ); auto structIt = findByNameOrAlias( m_structs, enable.first ); checkForError( structIt != m_structs.end(), enable.second.begin()->second, "unknown structure <" + enable.first + "> specified for SPIR-V capability <" + capability.first + ">" ); for ( auto const & member : enable.second ) { checkForError( std::ranges::any_of( structIt->second.members, [&member]( auto const & md ) { return md.name == member.first; } ), member.second, "unknown member <" + member.first + "> in struct <" + enable.first + "> specified for SPIR-V capability <" + capability.first + ">" ); } } } } void VulkanHppGenerator::checkStructCorrectness() const { std::set sTypeValues; for ( auto const & structure : m_structs ) { // check that a struct is referenced somewhere // I think, it's not forbidden to not reference a struct, but it would probably be not intended? auto typeIt = m_types.find( structure.first ); assert( typeIt != m_types.end() ); checkForError( !typeIt->second.requiredBy.empty(), structure.second.xmlLine, "structure <" + structure.first + "> not required by any feature or extension" ); // check for existence of all structs that are extended by this struct for ( auto const & extend : structure.second.structExtends ) { checkForError( findByNameOrAlias( m_structs, extend ) != m_structs.end(), structure.second.xmlLine, "struct <" + structure.first + "> extends unknown <" + extend + ">" ); } // checks on the members of a struct checkStructMemberCorrectness( structure.first, structure.second.members, sTypeValues ); } // enum VkStructureType checks (need to be after structure checks because of sTypeValues gathered there) auto structureTypeIt = m_enums.find( "VkStructureType" ); assert( structureTypeIt != m_enums.end() ); static std::set reservedValues = { "VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO", "VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO", "VK_STRUCTURE_TYPE_PRIVATE_VENDOR_INFO_PLACEHOLDER_OFFSET_0_NV" }; for ( auto const & enumValue : structureTypeIt->second.values ) { if ( reservedValues.contains( enumValue.name ) ) { checkForError( !sTypeValues.contains( enumValue.name ), enumValue.xmlLine, "Reserved VkStructureType enum value <" + enumValue.name + "> is used" ); } else { checkForError( !enumValue.supported || ( sTypeValues.erase( enumValue.name ) == 1 ), enumValue.xmlLine, "VkStructureType enum value <" + enumValue.name + "> never used" ); } } assert( sTypeValues.empty() ); } void VulkanHppGenerator::checkStructMemberArraySizesAreValid( std::vector const & arraySizes, int line ) const { // check that any array size is either a number or a known constant for ( auto const & arraySize : arraySizes ) { if ( !isNumber( arraySize ) && !m_constants.contains( arraySize ) ) { auto typeIt = m_types.find( arraySize ); checkForError( ( typeIt != m_types.end() ) && isUpperCase( arraySize ) && ( typeIt->second.category == TypeCategory::ExternalType ), line, "struct member array size uses unknown constant <" + arraySize + ">" ); } } } void VulkanHppGenerator::checkStructMemberCorrectness( std::string const & structureName, std::vector const & members, std::set & sTypeValues ) const { // determine if this struct is requird/used const bool structUsed = isTypeUsed( structureName ); for ( auto const & member : members ) { checkStructMemberArraySizesAreValid( member.arraySizes, member.xmlLine ); checkStructMemberSelectorConnection( member.selector, members, member.type.type ); checkStructMemberTypeIsKnown( member.type.type, member.xmlLine ); checkStructMemberTypeIsRequired( member.type.type, member.xmlLine, structureName ); checkStructMemberValueIsValid( member.value, member.type.type, member.name, member.xmlLine, structUsed, structureName, sTypeValues ); } } void VulkanHppGenerator::checkStructMemberSelectorConnection( std::string const & selector, std::vector const & members, std::string const & memberType ) const { // if a member specifies a selector, that member is a union and the selector is an enum // check that there's a 1-1 connection between the specified selections and the values of that enum if ( !selector.empty() ) { auto selectorIt = findByName( members, selector ); assert( selectorIt != members.end() ); auto selectorEnumIt = findByNameOrAlias( m_enums, selectorIt->type.type ); assert( selectorEnumIt != m_enums.end() ); auto unionIt = m_structs.find( memberType ); assert( ( unionIt != m_structs.end() ) && unionIt->second.isUnion ); for ( auto const & unionMember : unionIt->second.members ) { // check that each union member has a selection, that is a value of the seleting enum assert( !unionMember.selection.empty() ); for ( auto const & selection : unionMember.selection ) { checkForError( containsName( selectorEnumIt->second.values, selection ), unionMember.xmlLine, "union member <" + unionMember.name + "> uses selection <" + selection + "> that is not part of the selector type <" + selectorIt->type.type + ">" ); } } } } void VulkanHppGenerator::checkStructMemberTypeIsKnown( std::string const & memberType, int line ) const { // check that the member type is known checkForError( m_types.contains( memberType ), line, "struct member uses unknown type <" + memberType + ">" ); } void VulkanHppGenerator::checkStructMemberTypeIsRequired( std::string const & memberType, int line, std::string const & structureName ) const { // check that the member type is required in some feature or extension if ( memberType.starts_with( "Vk" ) ) { auto typeIt = m_types.find( memberType ); assert( typeIt != m_types.end() ); checkForError( !typeIt->second.requiredBy.empty(), line, "struct member type <" + memberType + "> used in struct <" + structureName + "> is never required for any feature or extension" ); } } void VulkanHppGenerator::checkStructMemberValueIsValid( std::string const & memberValue, std::string const & memberType, std::string const & memberName, int line, bool structUsed, std::string const & structureName, std::set & sTypeValues ) const { if ( !memberValue.empty() ) { auto enumIt = m_enums.find( memberType ); if ( enumIt != m_enums.end() ) { // check that the value exists in the specified enum (if the struct is used at all) checkForError( !structUsed || containsName( enumIt->second.values, memberValue ), line, "struct member <" + memberName + "> in structure <" + structureName + "> holds value <" + memberValue + "> of enum type <" + memberType + "> which is not listed" ); // special handling for sType: no value should appear more than once checkForError( !structUsed || ( memberName != "sType" ) || sTypeValues.insert( memberValue ).second, line, "sType value <" + memberValue + "> has been used before" ); } else if ( memberType == "uint32_t" ) { checkForError( isNumber( memberValue ), line, "value <" + memberValue + "> for member <" + memberName + "> in structure <" + structureName + "> of type <" + memberType + "> is not a number" ); } else { // don't know the type of the value -> error out checkForError( false, line, "member <" + memberName + "> in structure <" + structureName + "> holds value <" + memberValue + "> for an unhandled type <" + memberType + ">" ); } } } void VulkanHppGenerator::checkSyncAccessCorrectness() const { auto accessFlagBitsIt = m_enums.find( "VkAccessFlagBits" ); assert( accessFlagBitsIt != m_enums.end() ); auto accessFlagBits2It = m_enums.find( "VkAccessFlagBits2" ); assert( accessFlagBits2It != m_enums.end() ); for ( auto const & syncAccess : m_syncAccesses ) { auto nameIt = findByNameOrAlias( accessFlagBits2It->second.values, syncAccess.first ); checkForError( nameIt != accessFlagBits2It->second.values.end(), syncAccess.second.xmlLine, "syncaccess name <" + syncAccess.first + "> not specified as a VkAccessFlagBits value!" ); if ( !syncAccess.second.name.empty() ) { auto aliasIt = findByNameOrAlias( accessFlagBitsIt->second.values, syncAccess.second.name ); if ( aliasIt == accessFlagBitsIt->second.values.end() ) { aliasIt = findByNameOrAlias( accessFlagBits2It->second.values, syncAccess.second.name ); checkForError( aliasIt != accessFlagBits2It->second.values.end(), syncAccess.second.xmlLine, "syncaccess alias <" + syncAccess.second.name + "> not specified as a VkAccessFlagBits or a VkAccessFlagBits2 value!" ); } checkForError( ( aliasIt->value == nameIt->value ) && ( aliasIt->bitpos == nameIt->bitpos ), syncAccess.second.xmlLine, "syncaccess name <" + syncAccess.first + "> has an alias <" + syncAccess.second.name + "> with a different value or bitpos!" ); } } } void VulkanHppGenerator::checkSyncStageCorrectness() const { auto stageFlagBitsIt = m_enums.find( "VkPipelineStageFlagBits" ); assert( stageFlagBitsIt != m_enums.end() ); auto stageFlagBits2It = m_enums.find( "VkPipelineStageFlagBits2" ); assert( stageFlagBits2It != m_enums.end() ); for ( auto const & syncStage : m_syncStages ) { auto nameIt = findByNameOrAlias( stageFlagBits2It->second.values, syncStage.first ); checkForError( nameIt != stageFlagBits2It->second.values.end(), syncStage.second.xmlLine, "syncstage name <" + syncStage.first + "> not specified as a VkPipelineStageFlagBits2 value!" ); if ( !syncStage.second.name.empty() ) { // with alias auto aliasIt = findByNameOrAlias( stageFlagBitsIt->second.values, syncStage.second.name ); if ( aliasIt == stageFlagBitsIt->second.values.end() ) { aliasIt = findByNameOrAlias( stageFlagBits2It->second.values, syncStage.second.name ); checkForError( aliasIt != stageFlagBits2It->second.values.end(), syncStage.second.xmlLine, "syncstage alias <" + syncStage.second.name + "> not specified as a VkPipelineStageFlagBits or a VkPipelineStageFlagBits2 value!" ); } checkForError( ( aliasIt->value == nameIt->value ) && ( aliasIt->bitpos == nameIt->bitpos ), syncStage.second.xmlLine, "syncstate name <" + syncStage.first + "> has an alias <" + syncStage.second.name + "> with a different value or bitpos!" ); } } } bool VulkanHppGenerator::containsArray( std::string const & type ) const { // a simple recursive check if a type is or contains an array auto structureIt = m_structs.find( type ); return ( ( structureIt != m_structs.end() ) && std::ranges::any_of( structureIt->second.members, [this]( auto const & member ) { return !member.arraySizes.empty() || containsArray( member.type.type ); } ) ); } bool VulkanHppGenerator::containsDeprecated( std::vector const & members ) const { return std::ranges::any_of( members, []( auto const & member ) { return !member.deprecated.empty(); } ); } bool VulkanHppGenerator::containsFuncPointer( std::string const & type ) const { // a simple recursive check if a type contains a funcpointer auto structureIt = m_structs.find( type ); return ( structureIt != m_structs.end() ) && std::ranges::any_of( structureIt->second.members, [this, &type]( auto const & member ) { return m_funcPointers.contains( member.type.type ) || ( ( member.type.type != type ) && containsFuncPointer( member.type.type ) ); } ); } bool VulkanHppGenerator::containsFloatingPoints( std::vector const & members ) const { return std::ranges::any_of( members, [this]( auto const & member ) { auto structureIt = m_structs.find( member.type.type ); return member.type.isValue() && ( ( member.type.type == "float" ) || ( member.type.type == "double" ) || ( ( structureIt != m_structs.end() ) && containsFloatingPoints( structureIt->second.members ) ) ); } ); } bool VulkanHppGenerator::containsName( std::vector const & enumValues, std::string const & name ) const { return std::ranges::any_of( enumValues, [&name]( EnumValueData const & ev ) { return ( ev.name == name ) || std::ranges::any_of( ev.aliases, [&name]( auto const & eav ) { return eav.name == name; } ); } ); } bool VulkanHppGenerator::containsUnion( std::string const & type ) const { // a simple recursive check if a type is or contains a union auto structureIt = m_structs.find( type ); return ( structureIt != m_structs.end() ) && ( structureIt->second.isUnion || std::ranges::any_of( structureIt->second.members, [this]( auto const & member ) { return member.type.isValue() && containsUnion( member.type.type ); } ) ); } bool VulkanHppGenerator::describesVector( StructData const & structure, std::string const & type ) const { return std::ranges::any_of( structure.members, [&structure, &type]( auto const & member ) { if ( ( type.empty() || ( member.type.type == type ) ) && member.type.isNonConstPointer() && ( member.lenMembers.size() == 1 ) ) { assert( member.lenMembers[0].second < structure.members.size() ); auto const & lenMember = structure.members[member.lenMembers[0].second]; return lenMember.type.isValue() && ( ( lenMember.type.type == "size_t" ) || ( lenMember.type.type == "uint32_t" ) ); } return false; } ); } std::vector VulkanHppGenerator::determineChainedReturnParams( std::vector const & params, std::vector const & returnParams ) const { std::vector chainedParams; std::ranges::copy_if( returnParams, std::back_inserter( chainedParams ), [this, ¶ms]( size_t rp ) { return isStructureChainAnchor( params[rp].type.type ); } ); return chainedParams; } std::vector VulkanHppGenerator::determineDataTypes( std::vector const & params, std::map const & vectorParams, std::vector const & returnParams, std::set const & templatedParams, bool raii ) const { std::vector dataTypes; for ( auto rp : returnParams ) { auto vectorParamIt = vectorParams.find( rp ); if ( templatedParams.contains( rp ) ) { assert( ( vectorParamIt == vectorParams.end() ) || !vectorParamIt->second.byStructure ); if ( ( vectorParamIt != vectorParams.end() ) && ( params[rp].type.type == "void" ) && std::ranges::any_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->first; } ) && std::ranges::any_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->second.lenParam; } ) ) { // sized data of type void is returned as a std::array dataTypes.push_back( "uint8_t" ); } else { // for all other (templated) return parameters, the dataType is the name of the parameter with prefix "p" removed and "Type" appended dataTypes.push_back( ( stripPrefix( params[rp].name, "p" ) + "Type" ) ); } } else if ( ( vectorParamIt != vectorParams.end() ) && vectorParamIt->second.byStructure ) { // for non-templated vector parameters, we have to distinguish between those specified by a structure and those specified by a simple length parameter dataTypes.push_back( trimEnd( stripPostfix( vectorMemberByStructure( params[rp].type.type ).type.compose( "Vk", raii ? "VULKAN_HPP_NAMESPACE" : "" ), "*" ) ) ); } else { dataTypes.push_back( trimEnd( stripPostfix( params[rp].type.compose( "Vk", ( raii && m_handles.contains( params[rp].type.type ) ) ? "VULKAN_HPP_NAMESPACE" : "" ), "*" ) ) ); } } return dataTypes; } size_t VulkanHppGenerator::determineDefaultStartIndex( std::vector const & params, std::set const & skippedParams ) const { // determine the index where the arguments start to have defaults size_t defaultStartIndex = INVALID_INDEX; for ( int i = static_cast( params.size() ) - 1; ( 0 <= i ) && ( params[i].optional || skippedParams.contains( i ) ); --i ) { defaultStartIndex = i; } return defaultStartIndex; } bool VulkanHppGenerator::determineEnumeration( std::map const & vectorParams, std::vector const & returnParams ) const { // a command is considered to be enumerating some data, if for at least one vectorParam the data is a returnParam and either the vectorParams is // specified by a structure or the lenParam is a returnParam as well return std::ranges::any_of( vectorParams, [&returnParams]( auto const & vp ) { return std::ranges::any_of( returnParams, [&vp]( size_t rp ) { return rp == vp.first; } ) && ( vp.second.byStructure || std::ranges::any_of( returnParams, [&vp]( size_t rp ) { return rp == vp.second.lenParam; } ) ); } ); } size_t VulkanHppGenerator::determineInitialSkipCount( std::string const & command ) const { // determine the number of arguments to skip for a function // -> 0: the command is not bound to an instance or a device (the corresponding handle has no name) // -> 1: the command is bound to an instance or a device (the corresponding handle has a name) // -> 2: the command has been moved to a second handle auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); auto handleIt = m_handles.find( commandIt->second.handle ); assert( handleIt != m_handles.end() ); if ( !handleIt->second.commands.contains( command ) ) { assert( 1 < commandIt->second.params.size() ); assert( m_handles.contains( commandIt->second.params[1].type.type ) ); return 2; } else { return handleIt->first.empty() ? 0 : 1; } } std::vector VulkanHppGenerator::determineReturnParams( std::vector const & params ) const { std::vector returnParams; for ( size_t i = 0; i < params.size(); i++ ) { // very special handling of parameters of some types, which always come as a non-const pointer but are not meant // to be a potential return value! if ( params[i].type.isNonConstPointer() && !specialPointerTypes.contains( params[i].type.type ) ) { returnParams.push_back( i ); } } return returnParams; } bool VulkanHppGenerator::isConstructorCandidate( std::pair const & command, std::string const & handleType ) const { return isSupported( command.second.requiredBy ) && std::ranges::any_of( command.second.params, [this, &handleType]( ParamData const & pd ) { return isConstructorCandidate( pd, handleType ); } ); } bool VulkanHppGenerator::isConstructorCandidate( ParamData const & paramData, std::string const & handleType ) const { if ( paramData.type.isNonConstPointer() ) { if ( paramData.type.type == handleType ) { return true; } else { auto structIt = m_structs.find( paramData.type.type ); if ( structIt != m_structs.end() ) { return describesVector( structIt->second, handleType ); } } } return false; } std::vector::const_iterator> VulkanHppGenerator::determineRAIIHandleConstructors( std::string const & handleType, std::map::const_iterator destructorIt ) const { std::vector::const_iterator> constructorIts; for ( auto commandIt = m_commands.begin(); commandIt != m_commands.end(); ++commandIt ) { if ( isConstructorCandidate( *commandIt, handleType ) ) { // only commands that provide all information needed for the destructor can be considered a constructor! bool valid = true; if ( destructorIt != m_commands.end() ) { // get the destructors parameter to the handleType auto destructorHandleParamIt = std::ranges::find_if( destructorIt->second.params, [&handleType]( ParamData const & pd ) { return pd.type.type == handleType; } ); assert( destructorHandleParamIt != destructorIt->second.params.end() ); // lambda to check if a destructor parameter is a parameter of the constructor candidate // (or it's just the len parameter, which is not needed for the constructor) auto isConstructorCandidateParam = [&destructorHandleParamIt, &commandIt, this]( ParamData const & destructorParam ) { // check if the destructor param type equals this param type, or, if this param type is a struct, is part of // that struct auto isDestructorParamType = [&destructorParam, this]( ParamData const & pd ) { if ( pd.type.type != destructorParam.type.type ) { // check if the destructor param type equals a structure member type auto structureIt = m_structs.find( pd.type.type ); return ( structureIt != m_structs.end() ) && ( findByType( structureIt->second.members, destructorParam.type.type ) != structureIt->second.members.end() ); } return true; }; return ( destructorParam.name == destructorHandleParamIt->lenExpression ) || std::ranges::any_of( commandIt->second.params, isDestructorParamType ); }; // the constructor candidate is valid, if none of the (relevant) destructor parameters is missing in the // constructor candidate params valid = std::ranges::all_of( destructorIt->second.params, isConstructorCandidateParam ); } if ( valid ) { constructorIts.push_back( commandIt ); } } } return constructorIts; } std::map::const_iterator VulkanHppGenerator::determineRAIIHandleDestructor( std::string const & handleType ) const { std::string type = stripPrefix( handleType, "Vk" ); auto destructorIt = m_commands.find( "vkDestroy" + type ); if ( destructorIt == m_commands.end() ) { destructorIt = m_commands.find( "vkFree" + type + "s" ); if ( destructorIt == m_commands.end() ) { destructorIt = m_commands.find( "vkRelease" + type ); if ( destructorIt == m_commands.end() ) { if ( handleType == "VkDeviceMemory" ) { // special handling for vkDeviceMemory destructorIt = m_commands.find( "vkFreeMemory" ); assert( destructorIt != m_commands.end() ); } else if ( handleType == "VkDisplayKHR" ) { // special handling for VkDisplayKHR destructorIt = m_commands.find( "vkReleaseDisplayEXT" ); assert( destructorIt != m_commands.end() ); } else { assert( isEnumerated( handleType ) || ( handleType == "VkDisplayModeKHR" ) || ( handleType == "VkQueue" ) ); } } } } return destructorIt; } std::set VulkanHppGenerator::determineSingularParams( size_t returnParam, std::map const & vectorParams ) const { auto returnVectorIt = vectorParams.find( returnParam ); assert( returnVectorIt != vectorParams.end() ); std::set singularParams; singularParams.insert( returnVectorIt->second.lenParam ); for ( auto const & vpi : vectorParams ) { if ( vpi.second.lenParam == returnVectorIt->second.lenParam ) { singularParams.insert( vpi.first ); } } return singularParams; } std::set VulkanHppGenerator::determineSkippedParams( std::vector const & params, size_t initialSkipCount, std::map const & vectorParams, std::vector const & returnParams, bool singular ) const { // skip the initial skips (get fed by the object) assert( initialSkipCount <= params.size() ); std::set skippedParams; for ( size_t i = 0; i < initialSkipCount; ++i ) { skippedParams.insert( i ); } // skip the size parameters (get derived from an array), and a stride parameter for ( auto const & vpi : vectorParams ) { if ( !vpi.second.byStructure ) { assert( !params[vpi.first].lenExpression.empty() ); if ( ( std::ranges::none_of( returnParams, [&vpi]( size_t rpi ) { return vpi.first == rpi; } ) && isParam( params[vpi.first].lenExpression, params ) ) || ( singular && params[vpi.second.lenParam].type.isValue() ) ) { skippedParams.insert( vpi.second.lenParam ); } if ( vpi.second.strideParam != INVALID_INDEX ) { skippedParams.insert( vpi.second.strideParam ); } } } // skip the return parameters (get resolved by local variables to be returned) skippedParams.insert( returnParams.begin(), returnParams.end() ); return skippedParams; } std::string VulkanHppGenerator::determineSubStruct( std::pair const & structure ) const { if ( structure.second.members.front().name != "sType" ) { // check if sd is a substruct of structure auto isSubStruct = [&structure]( std::pair const & sd ) noexcept { // member-by-member comparison of type and name auto memberIt = structure.second.members.begin(); auto isMember = [&memberIt]( MemberData const & md ) noexcept { if ( ( md.type == memberIt->type ) && ( md.name == memberIt->name ) ) { ++memberIt; return true; } return false; }; return ( sd.second.members.size() < structure.second.members.size() ) && std::ranges::all_of( sd.second.members, isMember ); }; // look for a struct in m_structs that starts identically to structure auto structIt = std::ranges::find_if( m_structs, isSubStruct ); return ( structIt == m_structs.end() ) ? "" : structIt->first; } return ""; } std::map VulkanHppGenerator::determineVectorParams( std::vector const & params ) const { std::map vectorParams; // look for the parameters whose len equals the name of an other parameter for ( size_t i = 0; i < params.size(); i++ ) { if ( !params[i].lenExpression.empty() && ( params[i].lenExpression != "null-terminated" ) && ( params[i].lenExpression != "1" ) ) { VectorParamData & vpd = vectorParams[i]; if ( params[i].lenParams.empty() ) { std::string const & lenExpression = params[i].lenExpression; assert( std::ranges::none_of( params, [&lenExpression]( auto const & pd ) { return ( lenExpression == pd.name ); } ) ); auto lenIt = std::ranges::find_if( params, [this, &lenExpression]( auto const & pd ) { return isLenByStructMember( lenExpression, pd ); } ); assert( lenIt != params.end() ); vpd.lenParam = std::distance( params.begin(), lenIt ); } else { assert( params[i].lenParams.size() == 1 ); vpd.lenParam = params[i].lenParams[0].second; } if ( !params[i].strideParam.first.empty() ) { vpd.strideParam = params[i].strideParam.second; } } else { auto structIt = m_structs.find( params[i].type.type ); if ( ( structIt != m_structs.end() ) && !isStructureChainAnchor( params[i].type.type ) && describesVector( structIt->second ) ) { VectorParamData & vpd = vectorParams[i]; vpd.byStructure = true; vpd.lenParam = i; } } } return vectorParams; } std::set VulkanHppGenerator::determineVoidPointerParams( std::vector const & params ) const { std::set voidPointerParams; for ( size_t i = 0; i < params.size(); i++ ) { if ( !params[i].type.isValue() && ( params[i].type.type == "void" ) && ( params[i].type.postfix != "**" ) ) { voidPointerParams.insert( i ); } } return voidPointerParams; } void VulkanHppGenerator::distributeEnumValueAliases() { for ( auto & e : m_enums ) { for ( auto & a : e.second.valueAliases ) { auto valueIt = findByName( e.second.values, a.alias ); if ( valueIt == e.second.values.end() ) { auto aliasIt = findByName( e.second.valueAliases, a.alias ); checkForError( aliasIt != e.second.valueAliases.end(), a.xmlLine, "enum value alias <" + a.name + "> aliases non-existent enum value <" + a.alias + ">" ); valueIt = findByName( e.second.values, aliasIt->alias ); } checkForError( valueIt != e.second.values.end(), a.xmlLine, "enum value alias <" + a.name + "> aliases non-existent enum value <" + a.alias + ">" ); checkForError( a.protect == valueIt->protect, a.xmlLine, "enum value alias <" + a.name + "> aliases enum value <" + a.alias + "> with different properties" ); checkForError( std::ranges::none_of( valueIt->aliases, [&a]( auto const & eav ) { return a.name == eav.name; } ), a.xmlLine, "enum value alias <" + a.name + "> already contained as alias for enum value <" + a.alias + ">" ); valueIt->aliases.push_back( a ); } e.second.valueAliases.clear(); } } void VulkanHppGenerator::distributeRequirements() { for ( auto const & feature : m_features ) { distributeRequirements( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { distributeRequirements( extension.requireData, extension.name ); distributeRequirements( extension.unsupportedRequireData, extension.name ); } for ( auto const & feature : m_unsupportedFeatures ) { distributeRequirements( feature.requireData, feature.name ); } for ( auto const & extension : m_unsupportedExtensions ) { distributeRequirements( extension.requireData, extension.name ); distributeRequirements( extension.unsupportedRequireData, extension.name ); } } void VulkanHppGenerator::distributeRequirements( std::vector const & requireData, std::string const & requiredBy ) { for ( auto const & require : requireData ) { for ( auto const & command : require.commands ) { auto commandIt = findByNameOrAlias( m_commands, command.name ); checkForError( commandIt != m_commands.end(), command.xmlLine, "unknown required command <" + command.name + ">" ); commandIt->second.requiredBy.insert( requiredBy ); } for ( auto const & type : require.types ) { auto typeIt = m_types.find( type.name ); checkForError( typeIt != m_types.end(), type.xmlLine, "unknown required type <" + type.name + ">" ); typeIt->second.requiredBy.insert( requiredBy ); } } } void VulkanHppGenerator::distributeStructAliases() { for ( auto const & alias : m_structsAliases ) { auto structIt = findByNameOrAlias( m_structs, alias.second.name ); checkForError( structIt != m_structs.end(), alias.second.xmlLine, "struct alias <" + alias.first + "> references an unknown struct <" + alias.second.name + ">" ); checkForError( structIt->second.aliases.insert( { alias.first, alias.second.xmlLine } ).second, alias.second.xmlLine, "struct alias <" + alias.first + "> already listed as an alias for struct <" + alias.second.name + ">" ); } m_structsAliases.clear(); } bool VulkanHppGenerator::encodesEnumeration( std::vector const & params, std::map const & vectorParams, size_t returnParam0, size_t returnParam1 ) const { auto vpIt = vectorParams.find( returnParam1 ); return params[returnParam0].type.isNonConstPointer() && ( ( params[returnParam0].type.type == "size_t" ) || ( params[returnParam0].type.type == "uint32_t" ) ) && params[returnParam1].type.isNonConstPointer() && ( params[returnParam1].lenExpression == params[returnParam0].name ) && ( vpIt != vectorParams.end() ) && ( vpIt->second.lenParam == returnParam0 ) && !vpIt->second.byStructure && ( vpIt->second.strideParam == INVALID_INDEX ); } void VulkanHppGenerator::extendSpecialCommands( std::string const & name, bool definition, bool raii, std::string & cmd ) const { if ( name == "vkSetDebugUtilsObjectNameEXT" ) { if ( raii ) { if ( definition ) { cmd.append( R"( // wrapper function for command vkSetDebugUtilsObjectNameEXT, see // https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectNameEXT.html template VULKAN_HPP_INLINE typename ResultValueType::type Device::setDebugUtilsObjectNameEXT( HandleType const & handle, std::string const & name ) const { VULKAN_HPP_STATIC_ASSERT( VULKAN_HPP_NAMESPACE::isVulkanHandleType::value, "HandleType must be a Vulkan handle type" ); // It might be, that neither constructors, nor setters, nor designated initializers are available... need to explicitly set member by member VULKAN_HPP_NAMESPACE::DebugUtilsObjectNameInfoEXT nameInfo; nameInfo.objectType = handle.objectType; nameInfo.objectHandle = reinterpret_cast( static_cast( handle ) ); nameInfo.pObjectName = name.c_str(); return setDebugUtilsObjectNameEXT( nameInfo ); } )" ); } else { cmd.append( R"( // wrapper function for command vkSetDebugUtilsObjectNameEXT, see // https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectNameEXT.html template typename ResultValueType::type setDebugUtilsObjectNameEXT( HandleType const & handle, std::string const & name ) const; )" ); } } else { static const std::string endOfCmd = "#endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */\n"; assert( cmd.ends_with( endOfCmd ) ); size_t pos = cmd.size() - endOfCmd.size(); if ( definition ) { cmd.insert( pos, R"( // wrapper function for command vkSetDebugUtilsObjectNameEXT, see https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectNameEXT.html template ::type> VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS VULKAN_HPP_INLINE typename ResultValueType::type Device::setDebugUtilsObjectNameEXT( HandleType const & handle, std::string const & name, Dispatch const & d ) const { VULKAN_HPP_STATIC_ASSERT( VULKAN_HPP_NAMESPACE::isVulkanHandleType::value, "HandleType must be a Vulkan handle type" ); // It might be, that neither constructors, nor setters, nor designated initializers are available... need to explicitly set member by member VULKAN_HPP_NAMESPACE::DebugUtilsObjectNameInfoEXT nameInfo; nameInfo.objectType = handle.objectType; nameInfo.objectHandle = reinterpret_cast( static_cast( handle ) ); nameInfo.pObjectName = name.c_str(); return setDebugUtilsObjectNameEXT( nameInfo, d ); } )" ); } else { cmd.insert( pos, R"( // wrapper function for command vkSetDebugUtilsObjectNameEXT, see // https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectNameEXT.html template ::type = true> VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS typename ResultValueType::type setDebugUtilsObjectNameEXT( HandleType const & handle, std::string const & name, Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const; )" ); } } } else if ( name == "vkSetDebugUtilsObjectTagEXT" ) { if ( raii ) { if ( definition ) { cmd.append( R"( // wrapper function for command vkSetDebugUtilsObjectTagEXT, see // https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectTagEXT.html template VULKAN_HPP_INLINE typename ResultValueType::type Device::setDebugUtilsObjectTagEXT( HandleType const & handle, uint64_t name, TagType const & tag ) const { VULKAN_HPP_STATIC_ASSERT( VULKAN_HPP_NAMESPACE::isVulkanHandleType::value, "HandleType must be a Vulkan handle type" ); // It might be, that neither constructors, nor setters, nor designated initializers are available... need to explicitly set member by member VULKAN_HPP_NAMESPACE::DebugUtilsObjectTagInfoEXT tagInfo; tagInfo.objectType = handle.objectType; tagInfo.objectHandle = reinterpret_cast( static_cast( handle ) ); tagInfo.tagName = name; tagInfo.tagSize = sizeof( TagType ); tagInfo.pTag = &tag; return setDebugUtilsObjectTagEXT( tagInfo ); } )" ); } else { cmd.append( R"( // wrapper function for command vkSetDebugUtilsObjectTagEXT, see // https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectTagEXT.html template typename ResultValueType::type setDebugUtilsObjectTagEXT( HandleType const & handle, uint64_t name, TagType const & tag ) const; )" ); } } else { static const std::string endOfCmd = "#endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */\n"; assert( cmd.ends_with( endOfCmd ) ); size_t pos = cmd.size() - endOfCmd.size(); if ( definition ) { cmd.insert( pos, R"( // wrapper function for command vkSetDebugUtilsObjectTagEXT, see https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectTagEXT.html template ::type> VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS VULKAN_HPP_INLINE typename ResultValueType::type Device::setDebugUtilsObjectTagEXT( HandleType const & handle, uint64_t name, TagType const & tag, Dispatch const & d ) const { VULKAN_HPP_STATIC_ASSERT( VULKAN_HPP_NAMESPACE::isVulkanHandleType::value, "HandleType must be a Vulkan handle type" ); // It might be, that neither constructors, nor setters, nor designated initializers are available... need to explicitly set member by member VULKAN_HPP_NAMESPACE::DebugUtilsObjectTagInfoEXT tagInfo; tagInfo.objectType = handle.objectType; tagInfo.objectHandle = reinterpret_cast( static_cast( handle ) ); tagInfo.tagName = name; tagInfo.tagSize = sizeof( TagType ); tagInfo.pTag = &tag; return setDebugUtilsObjectTagEXT( tagInfo, d ); } )" ); } else { cmd.insert( pos, R"( // wrapper function for command vkSetDebugUtilsObjectTagEXT, see // https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectTagEXT.html template ::type = true> VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS typename ResultValueType::type setDebugUtilsObjectTagEXT( HandleType const & handle, uint64_t name, TagType const & tag, Dispatch const & d VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT ) const; )" ); } } } } void VulkanHppGenerator::filterLenMembers() { for ( auto & sd : m_structs ) { for ( auto & member : sd.second.members ) { std::erase_if( member.lenMembers, [this]( std::pair const & nameIndex ) { return m_constants.contains( nameIndex.first ); } ); for ( auto & lenMember : member.lenMembers ) { if ( lenMember.second == static_cast( ~0 ) ) { auto lenMemberIt = findByName( sd.second.members, lenMember.first ); checkForError( lenMemberIt != sd.second.members.end(), member.xmlLine, "struct member <" + member.name + "> uses unknown len member <" + lenMember.first + "> in its \"altlen\" attribute <" + member.lenExpressions[0] + ">" ); lenMember.second = std::distance( sd.second.members.begin(), lenMemberIt ); } } } } } std::string VulkanHppGenerator::findTag( std::string const & name, std::string const & postfix ) const { auto tagIt = std::ranges::find_if( m_tags, [&name, &postfix]( std::pair const & t ) { return name.ends_with( t.first + postfix ); } ); return ( tagIt != m_tags.end() ) ? tagIt->first : ""; } std::vector::const_iterator VulkanHppGenerator::findHandleMember( std::vector const & memberData ) const { return std::ranges::find_if( memberData, [this]( auto const & md ) { return isHandleType( md.type.type ); } ); } std::vector::const_iterator VulkanHppGenerator::findVectorMember( std::vector const & memberData ) const { return std::ranges::find_if( memberData, []( auto const & md ) { return md.arraySizes.empty() && !md.lenMembers.empty(); } ); } void VulkanHppGenerator::forEachRequiredBitmask( std::vector const & requireData, std::set & encounteredBitmasks, std::function const & )> const & bitmaskAction ) const { for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { if ( auto bitmaskIt = m_bitmasks.find( type.name ); ( bitmaskIt != m_bitmasks.end() ) && encounteredBitmasks.insert( bitmaskIt->first ).second ) { bitmaskAction( *bitmaskIt ); } } } } void VulkanHppGenerator::forEachRequiredCommand( std::vector const & requireData, std::function const & )> const & commandAction ) const { for ( auto const & require : requireData ) { for ( auto const & command : require.commands ) { auto commandIt = findByNameOrAlias( m_commands, command.name ); checkForError( commandIt != m_commands.end(), command.xmlLine, "unknown required command <" + command.name + ">" ); commandAction( command, *commandIt ); } } } void VulkanHppGenerator::forEachRequiredConstant( std::vector const & requireData, std::set & encounteredConstants, std::function const & )> const & constantAction ) const { for ( auto const & require : requireData ) { for ( auto const & constant : require.constants ) { auto constantIt = m_constants.find( constant.name ); checkForError( constantIt != m_constants.end(), constant.xmlLine, "unknown required constant <" + constant.name + ">" ); if ( encounteredConstants.insert( constantIt->first ).second ) { constantAction( *constantIt ); } } } } void VulkanHppGenerator::forEachRequiredEnumConstant( std::vector const & requireData, std::set & encounteredEnumConstants, std::function const & enumConstantAction ) const { for ( auto const & require : requireData ) { for ( auto const & enumConstant : require.enumConstants ) { if ( encounteredEnumConstants.insert( enumConstant.name ).second ) { enumConstantAction( enumConstant ); } } } } void VulkanHppGenerator::forEachRequiredFuncPointer( std::vector const & requireData, std::function const & )> const & funcPointerAction ) const { for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { if ( auto funcPointerIt = m_funcPointers.find( type.name ); funcPointerIt != m_funcPointers.end() ) { funcPointerAction( *funcPointerIt ); } } } } void VulkanHppGenerator::forEachRequiredHandle( std::vector const & requireData, std::function const & )> const & handleAction ) const { for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { if ( auto handleIt = m_handles.find( type.name ); handleIt != m_handles.end() ) { handleAction( *handleIt ); } } } } void VulkanHppGenerator::forEachRequiredStruct( std::vector const & requireData, std::function const & )> const & structAction ) const { for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { if ( auto structIt = m_structs.find( type.name ); structIt != m_structs.end() ) { structAction( *structIt ); } } } } std::set VulkanHppGenerator::gatherResultCodes() const { auto resultIt = m_enums.find( "VkResult" ); assert( resultIt != m_enums.end() ); std::set resultCodes; for ( auto rc : resultIt->second.values ) { resultCodes.insert( rc.name ); std::ranges::for_each( rc.aliases, [&resultCodes]( EnumValueAlias const & eva ) { resultCodes.insert( eva.name ); } ); } return resultCodes; } std::pair VulkanHppGenerator::generateAllocatorTemplates( std::vector const & returnParams, std::vector const & returnDataTypes, std::map const & vectorParams, std::vector const & chainedReturnParams, CommandFlavourFlags flavourFlags, bool definition ) const { assert( returnParams.size() == returnDataTypes.size() ); assert( chainedReturnParams.size() <= 1 ); const bool chained = flavourFlags & CommandFlavourFlagBits::chained; const bool singular = flavourFlags & CommandFlavourFlagBits::singular; const bool unique = flavourFlags & CommandFlavourFlagBits::unique; std::string allocatorTemplates; if ( !singular ) { for ( size_t i = 0; i < returnParams.size(); i++ ) { if ( vectorParams.contains( returnParams[i] ) ) { if ( chained && !chainedReturnParams.empty() && ( chainedReturnParams[0] == returnParams[i] ) ) { allocatorTemplates += "typename StructureChainAllocator"; if ( !definition ) { allocatorTemplates += " = std::allocator"; } } else { allocatorTemplates += "typename " + startUpperCase( returnDataTypes[i] ) + "Allocator"; if ( !definition ) { allocatorTemplates += " = std::allocator<" + ( unique ? ( "UniqueHandle<" + returnDataTypes[i] + ", Dispatch>" ) : returnDataTypes[i] ) + ">"; } } allocatorTemplates += ", "; } } } std::string uniqueHandleAllocatorTemplates; if ( unique && !allocatorTemplates.empty() ) { uniqueHandleAllocatorTemplates = ", " + stripPostfix( allocatorTemplates, ", " ); allocatorTemplates.clear(); } return { allocatorTemplates, uniqueHandleAllocatorTemplates }; } std::string VulkanHppGenerator::generateArgumentListEnhanced( std::vector const & params, std::vector const & returnParams, std::map const & vectorParams, std::set const & skippedParams, std::set const & singularParams, std::set const & templatedParams, std::vector const & chainedReturnParams, bool raii, bool definition, CommandFlavourFlags flavourFlags, bool withDispatcher ) const { const bool withAllocators = flavourFlags & CommandFlavourFlagBits::withAllocator; const size_t defaultStartIndex = withAllocators ? ~0 : determineDefaultStartIndex( params, skippedParams ); std::vector arguments; for ( size_t i = 0; i < params.size(); ++i ) { if ( !skippedParams.contains( i ) ) { bool hasDefaultAssignment = false; std::string composedType = params[i].type.compose( "Vk", ( raii && m_handles.contains( params[i].type.type ) ) ? "VULKAN_HPP_NAMESPACE" : "" ); if ( singularParams.contains( i ) ) { assert( !params[i].optional ); assert( params[i].type.isConstPointer() && !params[i].lenExpression.empty() && !isLenByStructMember( params[i].lenExpression, params ) && params[i].type.type.starts_with( "Vk" ) ); assert( composedType.ends_with( " *" ) ); arguments.push_back( stripPostfix( composedType, " *" ) + " & " + stripPluralS( startLowerCase( stripPrefix( params[i].name, "p" ) ) ) ); } else if ( params[i].type.isConstPointer() ) { assert( composedType.ends_with( " *" ) ); std::string name = startLowerCase( stripPrefix( params[i].name, "p" ) ); if ( params[i].lenExpression.empty() ) { assert( withDispatcher || !isHandleType( params[i].type.type ) ); assert( !params[i].type.prefix.empty() && ( params[i].type.postfix == "*" ) ); assert( params[i].arraySizes.empty() ); if ( params[i].type.type == "void" ) { arguments.push_back( templatedParams.contains( i ) ? ( stripPrefix( params[i].name, "p" ) + "Type const & " + name ) : ( composedType + " " + params[i].name ) ); } else if ( params[i].optional ) { arguments.push_back( "Optional<" + stripPostfix( composedType, " *" ) + "> " + name + ( ( definition || withAllocators ) ? "" : " VULKAN_HPP_DEFAULT_ASSIGNMENT( nullptr )" ) ); hasDefaultAssignment = true; } else { arguments.push_back( stripPostfix( composedType, " *" ) + " & " + name ); } } else { // a const-pointer with a non-empty len is either null-terminated (aka a string) or represented by an // ArrayProxy assert( params[i].arraySizes.empty() ); if ( params[i].lenExpression == "null-terminated" ) { assert( params[i].type.type == "char" ); if ( params[i].optional ) { arguments.push_back( "Optional " + name + ( ( definition || withAllocators ) ? "" : " VULKAN_HPP_DEFAULT_ASSIGNMENT( nullptr )" ) ); hasDefaultAssignment = true; } else { arguments.push_back( "const std::string & " + name ); } } else { // an ArrayProxy also covers no data, so any optional flag can be ignored here std::string type = stripPostfix( composedType, " *" ); const size_t pos = type.find( "void" ); if ( pos != std::string::npos ) { type.replace( pos, 4, stripPrefix( params[i].name, "p" ) + "Type" ); } arguments.push_back( std::string( params[i].strideParam.first.empty() ? "" : "Strided" ) + "ArrayProxy<" + type + "> const & " + name ); if ( params[i].optional && !definition ) { assert( params[i].strideParam.first.empty() ); arguments.back() += " VULKAN_HPP_DEFAULT_ASSIGNMENT( nullptr )"; hasDefaultAssignment = true; } } } } else if ( params[i].type.isNonConstPointer() ) { if ( ( params[i].type.type == "void" ) && ( flavourFlags & CommandFlavourFlagBits::keepVoidPtr ) ) { arguments.push_back( composedType + " " + params[i].name ); } else { assert( withDispatcher || !isHandleType( params[i].type.type ) ); assert( params[i].lenExpression.empty() && !params[i].optional ); assert( composedType.ends_with( " *" ) ); arguments.push_back( stripPostfix( composedType, " *" ) + " & " + startLowerCase( stripPrefix( params[i].name, "p" ) ) ); } } else { assert( params[i].type.isValue() ); // parameters named "objectType" collide with the member variable -> append an _ to here std::string paramName = ( definition && ( params[i].name == "objectType" ) ) ? "objectType_" : params[i].name; arguments.push_back( composedType + " " + paramName + generateCArraySizes( params[i].arraySizes ) ); } arguments.back() += std::string( !definition && ( defaultStartIndex <= i ) && !hasDefaultAssignment ? " VULKAN_HPP_DEFAULT_ASSIGNMENT( {} )" : "" ); } } if ( withAllocators ) { if ( ( flavourFlags & CommandFlavourFlagBits::chained ) && needsStructureChainResize( vectorParams, chainedReturnParams ) ) { arguments.push_back( "StructureChainAllocator & structureChainAllocator" ); } else { for ( auto sp : skippedParams ) { if ( !params[sp].lenExpression.empty() ) { std::string type; if ( templatedParams.contains( sp ) ) { auto vectorParamIt = vectorParams.find( sp ); if ( ( vectorParamIt != vectorParams.end() ) && std::ranges::any_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->first; } ) && std::ranges::any_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->second.lenParam; } ) ) { type = "Uint8_t"; } else { type = stripPrefix( params[sp].name, "p" ) + "Type"; } } else { type = ( params[sp].type.type == "void" ) ? "Uint8_t" : startUpperCase( stripPrefix( params[sp].type.type, "Vk" ) ); } arguments.push_back( type + "Allocator const & " + startLowerCase( type ) + "Allocator" ); } else if ( vectorParams.contains( sp ) && vectorParams.find( sp )->second.byStructure ) { std::string type = stripPrefix( vectorMemberByStructure( params[sp].type.type ).type.type, "Vk" ); assert( isupper( type[0] ) ); arguments.push_back( type + "Allocator const & " + startLowerCase( type ) + "Allocator" ); } } } } if ( withDispatcher ) { arguments.push_back( std::string( "Dispatch const & d" ) + ( definition ? "" : " VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT" ) ); } return generateList( arguments, "", ", " ); } std::string VulkanHppGenerator::generateArgumentListStandard( std::vector const & params, std::set const & skippedParams, bool definition, bool raii, bool withDispatcher ) const { std::string argumentList; for ( size_t i = 0; i < params.size(); ++i ) { if ( !skippedParams.contains( i ) ) { // parameters named "objectType" collide with the member variable -> append an _ to here std::string paramName = ( definition && ( params[i].name == "objectType" ) ) ? "objectType_" : params[i].name; argumentList += params[i].type.compose( "Vk", ( raii && isHandleType( params[i].type.type ) ? "VULKAN_HPP_NAMESPACE" : "" ) ) + " " + paramName + generateCArraySizes( params[i].arraySizes ) + ", "; } } if ( withDispatcher ) { argumentList += "Dispatch const & d "; } else if ( !argumentList.empty() ) { assert( argumentList.ends_with( ", " ) ); argumentList = stripPostfix( argumentList, ", " ) + " "; } return argumentList; } std::string VulkanHppGenerator::generateArgumentTemplates( std::vector const & params, std::vector const & returnParams, std::map const & vectorParams, std::set const & templatedParams, std::vector const & chainedReturnParams, bool raii ) const { std::string argumentTemplates; if ( !chainedReturnParams.empty() ) { assert( chainedReturnParams.size() == 1 ); argumentTemplates = vectorParams.contains( chainedReturnParams[0] ) ? "typename StructureChain, " : "typename X, typename Y, typename... Z, "; } if ( !templatedParams.empty() ) { for ( auto t : templatedParams ) { assert( params[t].name.starts_with( "p" ) ); auto vectorParamIt = vectorParams.find( t ); if ( ( vectorParamIt == vectorParams.end() ) || std::ranges::none_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->first; } ) || std::ranges::none_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->second.lenParam; } ) ) { assert( chainedReturnParams.empty() ); // only templated parameters that are not part of an enumeration are really templated argumentTemplates += "typename " + stripPrefix( params[t].name, "p" ) + "Type, "; } } } if ( !argumentTemplates.empty() && raii ) { argumentTemplates = "template <" + stripPostfix( argumentTemplates, ", " ) + ">"; } return argumentTemplates; } std::string VulkanHppGenerator::generateBaseTypes() const { assert( !m_baseTypes.empty() ); const std::string basetypesTemplate = R"( //================== //=== BASE TYPEs === //================== ${basetypes} )"; std::string basetypes; for ( auto const & baseType : m_baseTypes ) { // filter out VkFlags and VkFlags64, as they are mapped to our own Flags class, and basetypes without any type information if ( ( baseType.first != "VkFlags" ) && ( baseType.first != "VkFlags64" ) && !baseType.second.typeInfo.type.empty() ) { basetypes += " using " + stripPrefix( baseType.first, "Vk" ) + " = " + baseType.second.typeInfo.compose( "Vk", "VULKAN_HPP_NAMESPACE" ) + ";\n"; } } return replaceWithMap( basetypesTemplate, { { "basetypes", basetypes } } ); } std::string VulkanHppGenerator::generateBitmask( std::map::const_iterator bitmaskIt, std::string const & surroundingProtect ) const { auto bitmaskBitsIt = m_enums.find( bitmaskIt->second.require ); assert( bitmaskBitsIt != m_enums.end() ); std::string bitmaskName = stripPrefix( bitmaskIt->first, "Vk" ); std::string enumName = stripPrefix( bitmaskBitsIt->first, "Vk" ); std::string allFlags, wrappedType; if ( bitmaskBitsIt->second.values.empty() || std::ranges::none_of( bitmaskBitsIt->second.values, []( auto const & evd ) { return evd.supported; } ) ) { allFlags = " {};"; } else { bool encounteredFlag = false; std::string previousEnter, previousLeave; for ( auto const & value : bitmaskBitsIt->second.values ) { if ( value.supported ) { // if the value's protect differs from the surrounding protect, generate protection code std::string enter, leave; if ( !value.protect.empty() && ( value.protect != surroundingProtect ) ) { tie( enter, leave ) = generateProtection( value.protect ); } std::string valueName = generateEnumValueName( bitmaskBitsIt->first, value.name, true ); allFlags += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + " " + ( encounteredFlag ? "| " : " " ) + enumName + "::" + valueName; encounteredFlag = true; previousEnter = enter; previousLeave = leave; } } if ( !previousLeave.empty() ) { allFlags += "\n" + previousLeave; } allFlags += ";"; wrappedType = "using WrappedType = " + bitmaskBitsIt->first + ";"; } std::string aliases; for ( auto const & a : bitmaskIt->second.aliases ) { aliases += " using " + stripPrefix( a.first, "Vk" ) + " = " + bitmaskName + ";\n"; } static const std::string bitmaskTemplate = R"( // wrapper using for bitmask Vk${bitmaskName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/Vk${bitmaskName}.html using ${bitmaskName} = Flags<${enumName}>; ${aliases} template <> struct FlagTraits<${enumName}> { ${WrappedType} static VULKAN_HPP_CONST_OR_CONSTEXPR bool isBitmask = true; static VULKAN_HPP_CONST_OR_CONSTEXPR ${bitmaskName} allFlags = ${allFlags} }; )"; return replaceWithMap( bitmaskTemplate, { { "aliases", aliases }, { "allFlags", allFlags }, { "bitmaskName", bitmaskName }, { "enumName", enumName }, { "WrappedType", wrappedType } } ); } std::string VulkanHppGenerator::generateBitmasksToString() const { const std::string bitmasksToStringTemplate = R"( //========================== //=== BITMASKs to_string === //========================== ${bitmasksToString} )"; std::string bitmasksToString; std::set listedBitmasks; for ( auto const & feature : m_features ) { bitmasksToString += generateBitmasksToString( feature.requireData, listedBitmasks, feature.name ); } for ( auto const & extension : m_extensions ) { bitmasksToString += generateBitmasksToString( extension.requireData, listedBitmasks, extension.name ); } return replaceWithMap( bitmasksToStringTemplate, { { "bitmasksToString", bitmasksToString } } ); } std::string VulkanHppGenerator::generateBitmasksToString( std::vector const & requireData, std::set & listedBitmasks, std::string const & title ) const { std::string str; forEachRequiredBitmask( requireData, listedBitmasks, [&str, this]( std::pair const & bitmaskData ) { str += generateBitmaskToString( bitmaskData ); } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateBitmaskToString( std::pair const & bitmaskData ) const { auto bitmaskBitsIt = m_enums.find( bitmaskData.second.require ); assert( bitmaskBitsIt != m_enums.end() ); std::string bitmaskName = stripPrefix( bitmaskData.first, "Vk" ); std::string enumName = stripPrefix( bitmaskBitsIt->first, "Vk" ); std::string str; std::string emptyValue = "{}"; std::string toStringChecks; std::string previousEnter, previousLeave; for ( auto const & value : bitmaskBitsIt->second.values ) { if ( value.supported ) { std::string valueName = generateEnumValueName( bitmaskBitsIt->first, value.name, true ); if ( value.value == "0" ) { assert( emptyValue == "{}" ); emptyValue = valueName.substr( 1 ); } else if ( !value.bitpos.empty() ) { const auto [enter, leave] = generateProtection( value.protect ); toStringChecks += ( ( previousEnter != enter ) ? ( previousLeave + enter ) : "" ) + " if ( value & " + enumName + "::" + valueName + " ) result += \" " + valueName.substr( 1 ) + " |\";\n"; previousEnter = enter; previousLeave = leave; } } } if ( !previousLeave.empty() ) { assert( previousLeave.ends_with( "\n" ) ); toStringChecks += previousLeave; previousLeave.resize( previousLeave.size() - strlen( "\n" ) ); } if ( toStringChecks.empty() ) { static const std::string bitmaskToStringTemplate = R"( VULKAN_HPP_INLINE VULKAN_HPP_CONSTEXPR_20 std::string to_string( ${bitmaskName} ) { return "${emptyValue}"; } )"; str += replaceWithMap( bitmaskToStringTemplate, { { "bitmaskName", bitmaskName }, { "emptyValue", emptyValue } } ); } else { static const std::string bitmaskToStringTemplate = R"( VULKAN_HPP_INLINE std::string to_string( ${bitmaskName} value ) { std::string result = "{"; ${toStringChecks} if ( result.size() > 1 ) result.back() = '}'; else result = "${emptyValue}"; return result; } )"; str += replaceWithMap( bitmaskToStringTemplate, { { "bitmaskName", bitmaskName }, { "emptyValue", emptyValue }, { "toStringChecks", toStringChecks } } ); } return str; } std::string VulkanHppGenerator::generateCallArgumentsEnhanced( CommandData const & commandData, size_t initialSkipCount, bool nonConstPointerAsNullptr, std::set const & singularParams, std::set const & templatedParams, bool raii, bool raiiFactory, CommandFlavourFlags flavourFlags ) const { assert( initialSkipCount <= commandData.params.size() ); std::vector arguments; if ( raii ) { switch ( initialSkipCount ) { case 0: break; case 1: assert( isHandleType( commandData.params[0].type.type ) && commandData.params[0].type.isValue() ); assert( commandData.params[0].arraySizes.empty() && commandData.params[0].lenExpression.empty() ); assert( commandData.params[0].type.type == commandData.handle ); arguments.push_back( "static_cast<" + commandData.handle + ">( m_" + startLowerCase( stripPrefix( commandData.handle, "Vk" ) ) + " )" ); break; case 2: { assert( isHandleType( commandData.params[0].type.type ) && commandData.params[0].type.isValue() ); assert( commandData.params[0].arraySizes.empty() && commandData.params[0].lenExpression.empty() ); assert( commandData.params[0].type.type == commandData.handle ); auto handleIt = m_handles.find( commandData.params[1].type.type ); assert( handleIt != m_handles.end() ); arguments.push_back( "static_cast<" + commandData.handle + ">( m_" + startLowerCase( stripPrefix( commandData.handle, "Vk" ) ) + " )" ); assert( commandData.params[1].type.isValue() && commandData.params[1].arraySizes.empty() && commandData.params[1].lenExpression.empty() ); arguments.push_back( "static_cast<" + commandData.params[1].type.type + ">( m_" + startLowerCase( stripPrefix( handleIt->first, "Vk" ) ) + " )" ); } break; default: assert( false ); break; } } else { for ( size_t i = 0; i < initialSkipCount; ++i ) { assert( isHandleType( commandData.params[i].type.type ) && commandData.params[i].type.isValue() ); assert( commandData.params[i].arraySizes.empty() && commandData.params[i].lenExpression.empty() ); arguments.push_back( "m_" + startLowerCase( stripPrefix( commandData.params[i].type.type, "Vk" ) ) ); } } for ( size_t i = initialSkipCount; i < commandData.params.size(); ++i ) { arguments.push_back( generateCallArgumentEnhanced( commandData.params, i, nonConstPointerAsNullptr, singularParams, templatedParams, flavourFlags, raiiFactory ) ); } return generateList( arguments, "", ", " ); } std::string VulkanHppGenerator::generateCallArgumentsStandard( std::vector const & params, size_t initialSkipCount ) const { std::vector arguments; for ( size_t i = 0; i < initialSkipCount; ++i ) { auto const & param = params[i]; assert( isHandleType( param.type.type ) && param.type.isValue() ); assert( param.arraySizes.empty() && param.lenExpression.empty() ); arguments.push_back( "static_cast<" + param.type.type + ">( m_" + startLowerCase( stripPrefix( param.type.type, "Vk" ) ) + " )" ); } for ( size_t i = initialSkipCount; i < params.size(); ++i ) { auto const & param = params[i]; std::string argument = param.name; if ( param.type.type.starts_with( "Vk" ) ) { if ( !param.arraySizes.empty() ) { assert( param.arraySizes.size() == 1 ); assert( param.type.isValue() ); assert( param.type.postfix.empty() ); argument = "reinterpret_cast<" + param.type.compose( "" ) + " *>( " + argument + " )"; } else if ( param.type.isValue() ) { // parameters named "objectType" collide with the member variable -> append an _ to here if ( argument == "objectType" ) { argument += "_"; } argument = "static_cast<" + param.type.type + ">( " + argument + " )"; } else { assert( !param.type.postfix.empty() ); argument = "reinterpret_cast<" + param.type.compose( "" ) + ">( " + argument + " )"; } } arguments.push_back( argument ); } return generateList( arguments, "", ", " ); } std::string VulkanHppGenerator::generateCallArgumentEnhanced( std::vector const & params, size_t paramIndex, bool nonConstPointerAsNullptr, std::set const & singularParams, std::set const & templatedParams, CommandFlavourFlags flavourFlags, bool raiiFactory ) const { std::string argument; ParamData const & param = params[paramIndex]; if ( param.type.isConstPointer() || specialPointerTypes.contains( param.type.type ) ) { // parameter is a const-pointer or one of the special pointer types that are considered to be const-pointers argument = generateCallArgumentEnhancedConstPointer( param, paramIndex, singularParams, templatedParams ); } else if ( param.type.isNonConstPointer() && !specialPointerTypes.contains( param.type.type ) && !( ( param.type.type == "void" ) && ( flavourFlags & CommandFlavourFlagBits::keepVoidPtr ) ) ) { // parameter is a non-const pointer, none of the special pointer types, that are considered const-pointers, and no void-ptr to keep argument = generateCallArgumentEnhancedNonConstPointer( param, paramIndex, nonConstPointerAsNullptr, singularParams ); } else { argument = generateCallArgumentEnhancedValue( params, paramIndex, singularParams, flavourFlags, raiiFactory ); } assert( !argument.empty() ); return argument; } std::string VulkanHppGenerator::generateCallArgumentEnhancedConstPointer( ParamData const & param, size_t paramIndex, std::set const & singularParams, std::set const & templatedParams ) const { std::string argument; std::string name = startLowerCase( stripPrefix( param.name, "p" ) ); if ( isHandleType( param.type.type ) && param.type.isValue() ) { assert( !param.optional ); // if at all, this is the first argument, and it's the implicitly provided member handle assert( paramIndex == 0 ); assert( param.arraySizes.empty() && param.lenExpression.empty() ); argument = "m_" + startLowerCase( stripPrefix( param.type.type, "Vk" ) ); } else if ( param.lenExpression.empty() ) { // this const-pointer parameter has no length, that is it's a const-pointer to a single value if ( param.type.type == "void" ) { argument = templatedParams.contains( paramIndex ) ? "reinterpret_cast<" + param.type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ) + ">( &" + name + " )" : param.name; } else if ( param.optional ) { argument = name + ".get()"; } else { argument = "&" + name; } if ( param.type.type.starts_with( "Vk" ) ) { argument = "reinterpret_cast<" + param.type.compose( "" ) + ">( " + argument + " )"; } } else if ( param.lenExpression == "null-terminated" ) { // this const-pointer parameter is "null-terminated", that is it's a string assert( ( param.type.type == "char" ) && param.arraySizes.empty() ); if ( param.optional ) { argument = name + " ? " + name + "->c_str() : nullptr"; } else { argument = name + ".c_str()"; } } else { // this const-pointer parameter has some explicit length if ( singularParams.contains( paramIndex ) ) { assert( !param.optional ); argument = "&" + stripPluralS( name ); } else { // this const-parameter is represented by some array, where data() also works with no data (optional) argument = name + ".data()"; } if ( param.type.type.starts_with( "Vk" ) || ( param.type.type == "void" ) ) { argument = "reinterpret_cast<" + param.type.compose( "" ) + ">( " + argument + " )"; } } return argument; } std::string VulkanHppGenerator::generateCallArgumentEnhancedNonConstPointer( ParamData const & param, size_t paramIndex, bool nonConstPointerAsNullptr, std::set const & singularParams ) const { std::string argument; std::string name = startLowerCase( stripPrefix( param.name, "p" ) ); if ( param.lenExpression.empty() || ( param.lenExpression == "1" ) ) { assert( param.arraySizes.empty() ); if ( param.type.type.starts_with( "Vk" ) ) { argument = "reinterpret_cast<" + param.type.compose( "" ) + ">( &" + name + " )"; } else { argument = "&" + name; } } else { // the non-const pointer has a lenExpression -> it will be represented by some array assert( param.arraySizes.empty() ); if ( nonConstPointerAsNullptr ) { argument = "nullptr"; } else { if ( singularParams.contains( paramIndex ) ) { argument = "&" + stripPluralS( name ); } else { // get the data of the array, which also covers no data -> no need to look at param.optional argument = name + ".data()"; } if ( param.type.type.starts_with( "Vk" ) || ( param.type.type == "void" ) ) { argument = "reinterpret_cast<" + param.type.compose( "" ) + ">( " + argument + " )"; } } } return argument; } std::string VulkanHppGenerator::generateCallArgumentEnhancedValue( std::vector const & params, size_t paramIndex, std::set const & singularParams, CommandFlavourFlags flavourFlags, bool raiiFactory ) const { std::string argument; ParamData const & param = params[paramIndex]; assert( ( flavourFlags & CommandFlavourFlagBits::keepVoidPtr ) || param.lenExpression.empty() ); // !keepVoidPtr -> no lenExpression if ( param.type.type.starts_with( "Vk" ) ) { if ( param.arraySizes.empty() ) { // check if this param is used as the len of an other param auto pointerIt = std::ranges::find_if( params, [¶m]( ParamData const & pd ) { return pd.lenExpression == param.name; } ); if ( pointerIt != params.end() ) { assert( !param.optional ); argument = startLowerCase( stripPrefix( pointerIt->name, "p" ) ) + ".size()"; if ( pointerIt->type.type == "void" ) { argument += " * sizeof( " + stripPrefix( pointerIt->name, "p" ) + "Type )"; } } else if ( raiiFactory && m_handles.contains( param.type.type ) ) { assert( param.type.isValue() && param.lenExpression.empty() ); if ( param.optional ) { argument = param.name + " ? static_cast<" + param.type.type + ">( **" + param.name + " ) : 0"; } else { argument = "static_cast<" + param.type.type + ">( *" + param.name + " )"; } } else { assert( param.type.isValue() ); // parameters named "objectType" collide with the member variable -> append an _ to here std::string paramName = ( param.name == "objectType" ) ? "objectType_" : param.name; argument = "static_cast<" + param.type.type + ">( " + paramName + " )"; } // check if this param is used as the stride of an other param assert( std::ranges::none_of( params, [paramIndex]( ParamData const & pd ) { return pd.strideParam.second == paramIndex; } ) ); } else { assert( !param.optional ); assert( param.arraySizes.size() == 1 ); assert( param.type.prefix == "const" ); argument = "reinterpret_cast<" + param.type.compose( "" ) + " *>( " + param.name + " )"; } } else { auto pointerIt = std::ranges::find_if( params, [¶m]( ParamData const & pd ) { return pd.lenExpression == param.name; } ); if ( ( pointerIt != params.end() ) && !( ( pointerIt->type.type == "void" ) && ( flavourFlags & CommandFlavourFlagBits::keepVoidPtr ) ) ) { // this parameter is the len of some other -> replace it with that parameter's size assert( param.arraySizes.empty() ); assert( ( param.type.type == "size_t" ) || ( param.type.type == "uint32_t" ) ); if ( singularParams.contains( paramIndex ) ) { if ( pointerIt->type.type == "void" ) { argument = "sizeof( " + stripPrefix( pointerIt->name, "p" ) + "Type )"; } else { argument = "1"; } } else { argument = startLowerCase( stripPrefix( pointerIt->name, "p" ) ) + ".size()"; if ( pointerIt->type.type == "void" ) { argument += " * sizeof( " + stripPrefix( pointerIt->name, "p" ) + "Type )"; } } } else { assert( !param.optional ); assert( param.arraySizes.size() <= 1 ); pointerIt = std::ranges::find_if( params, [paramIndex]( ParamData const & pd ) { return pd.strideParam.second == paramIndex; } ); if ( pointerIt != params.end() ) { // this parameter is the stride of some other -> replace it with that parameter's stride assert( param.arraySizes.empty() ); assert( param.type.type == "uint32_t" ); assert( pointerIt->strideParam.first == param.name ); argument = startLowerCase( stripPrefix( pointerIt->name, "p" ) ) + ".stride()"; } else { argument = param.name; } } } return argument; } std::string VulkanHppGenerator::generateCallSequence( std::string const & name, CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, size_t initialSkipCount, std::set const & singularParams, std::set const & templatedParams, std::vector const & chainedReturnParams, CommandFlavourFlags flavourFlags, bool raii, bool raiiFactory ) const { std::string dispatcher = raii ? "getDispatcher()->" : "d."; // first some special handling on vkCreatePipelineBinariesKHR !! if ( name == "vkCreatePipelineBinariesKHR" ) { #if !defined( NDEBUG ) auto paramIt = std::ranges::find_if( commandData.params, []( ParamData const & pd ) { return pd.name == "pCreateInfo"; } ); assert( paramIt != commandData.params.end() && ( paramIt->type.type == "VkPipelineBinaryCreateInfoKHR" ) ); auto structIt = m_structs.find( "VkPipelineBinaryCreateInfoKHR" ); assert( ( structIt != m_structs.end() ) && std::ranges::any_of( structIt->second.members, []( MemberData const & md ) { return md.name == "pipeline"; } ) && std::ranges::any_of( structIt->second.members, []( MemberData const & md ) { return md.name == "pPipelineCreateInfo"; } ) ); auto memberIt = std::ranges::find_if( structIt->second.members, []( MemberData const & md ) { return md.name == "pKeysAndDataInfo"; } ); assert( memberIt != structIt->second.members.end() && ( memberIt->type.type == "VkPipelineBinaryKeysAndDataKHR" ) ); structIt = m_structs.find( "VkPipelineBinaryKeysAndDataKHR" ); assert( ( structIt != m_structs.end() ) && std::ranges::any_of( structIt->second.members, []( MemberData const & md ) { return md.name == "binaryCount"; } ) ); structIt = m_structs.find( "VkPipelineBinaryHandlesInfoKHR" ); assert( ( structIt != m_structs.end() ) && std::ranges::any_of( structIt->second.members, []( MemberData const & md ) { return md.name == "pipelineBinaryCount"; } ) && std::ranges::any_of( structIt->second.members, []( MemberData const & md ) { return md.name == "pPipelineBinaries"; } ) ); #endif const std::string callSequenceTemplate = R"( Result result; if ( createInfo.pKeysAndDataInfo ) { VULKAN_HPP_ASSERT( !createInfo.pipeline && !createInfo.pPipelineCreateInfo ); pipelineBinaries.resize( createInfo.pKeysAndDataInfo->binaryCount ); binaries.pipelineBinaryCount = createInfo.pKeysAndDataInfo->binaryCount; binaries.pPipelineBinaries = pipelineBinaries.data(); result = static_cast( ${dispatcher}${vkCommand}( ${callArguments} ) ); } else { VULKAN_HPP_ASSERT( !createInfo.pipeline ^ !createInfo.pPipelineCreateInfo ); result = static_cast( ${dispatcher}${vkCommand}( ${callArguments} ) ); if ( result == Result::eSuccess ) { pipelineBinaries.resize( binaries.pipelineBinaryCount ); binaries.pPipelineBinaries = pipelineBinaries.data(); result = static_cast( ${dispatcher}${vkCommand}( ${callArguments} ) ); } } )"; std::string callArguments = generateCallArgumentsEnhanced( commandData, initialSkipCount, false, singularParams, templatedParams, raii, raiiFactory, flavourFlags ); return replaceWithMap( callSequenceTemplate, { { "callArguments", callArguments }, { "dispatcher", dispatcher }, { "vkCommand", name } } ); } // if at least one returnParam is a size value of a vector param (and no singular params), we need two calls if ( singularParams.empty() && std::ranges::any_of( returnParams, [&vectorParams]( size_t rp ) { return std::ranges::any_of( vectorParams, [rp]( auto const & vp ) { return vp.second.lenParam == rp; } ); } ) ) { auto vectorParamIt = vectorParams.begin(); assert( std::ranges::any_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->first; } ) ); assert( std::ranges::any_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->second.lenParam; } ) ); std::string firstCallArguments = generateCallArgumentsEnhanced( commandData, initialSkipCount, true, {}, templatedParams, raii, raiiFactory, flavourFlags ); std::string secondCallArguments = generateCallArgumentsEnhanced( commandData, initialSkipCount, false, {}, templatedParams, raii, raiiFactory, flavourFlags ); std::string vectorName, vectorSize; if ( vectorParamIt->second.byStructure ) { auto structIt = m_structs.find( commandData.params[vectorParamIt->first].type.type ); assert( structIt != m_structs.end() ); vectorName = structIt->second.members.back().name; vectorSize = startLowerCase( stripPrefix( commandData.params[vectorParamIt->first].name, "p" ) ) + "." + structIt->second.members[vectorParamIt->second.lenParam].name; } else { vectorName = commandData.params[vectorParamIt->first].name; vectorSize = startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ); } vectorName = startLowerCase( stripPrefix( vectorName, "p" ) ); if ( ( flavourFlags & CommandFlavourFlagBits::chained ) && needsStructureChainResize( vectorParams, chainedReturnParams ) ) { assert( vectorParams.size() == 1 ); // chained data needs some more handling!! std::string vectorElementType = stripPostfix( commandData.params[vectorParamIt->first].type.compose( "Vk" ), " *" ); if ( commandData.returnType.type == "VkResult" ) { const std::string callSequenceTemplate = R"(Result result; do { result = static_cast( ${dispatcher}${vkCommand}( ${firstCallArguments} ) ); if ( ( result == Result::eSuccess ) && ${counterName} ) { structureChains.resize( ${counterName} ); ${vectorName}.resize( ${counterName} ); for ( ${counterType} i = 0; i < ${counterName}; i++ ) { ${vectorName}[i].pNext = structureChains[i].template get<${vectorElementType}>().pNext; } result = static_cast( ${dispatcher}${vkCommand}( ${secondCallArguments} ) ); } } while ( result == Result::eIncomplete );)"; return replaceWithMap( callSequenceTemplate, { { "counterName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ) }, { "counterType", commandData.params[vectorParamIt->second.lenParam].type.type }, { "dispatcher", dispatcher }, { "firstCallArguments", firstCallArguments }, { "secondCallArguments", secondCallArguments }, { "vectorElementType", vectorElementType }, { "vectorName", vectorName }, { "vkCommand", name } } ); } else { const std::string callSequenceTemplate = R"(${dispatcher}${vkCommand}( ${firstCallArguments} ); structureChains.resize( ${counterName} ); ${vectorName}.resize( ${counterName} ); for ( ${counterType} i = 0; i < ${counterName}; i++ ) { ${vectorName}[i].pNext = structureChains[i].template get<${vectorElementType}>().pNext; } ${dispatcher}${vkCommand}( ${secondCallArguments} );)"; return replaceWithMap( callSequenceTemplate, { { "counterName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ) }, { "counterType", commandData.params[vectorParamIt->second.lenParam].type.type }, { "dispatcher", dispatcher }, { "firstCallArguments", firstCallArguments }, { "secondCallArguments", secondCallArguments }, { "vectorElementType", vectorElementType }, { "vectorName", vectorName }, { "vkCommand", name } } ); } } else if ( commandData.returnType.type == "VkResult" ) { std::string resizes; for ( auto const & vp : vectorParams ) { assert( std::ranges::any_of( returnParams, [&vp]( size_t rp ) { return rp == vp.first; } ) && std::ranges::any_of( returnParams, [&vp]( size_t rp ) { return rp == vp.second.lenParam; } ) ); resizes += startLowerCase( stripPrefix( commandData.params[vp.first].name, "p" ) ) + ".resize( " + startLowerCase( stripPrefix( commandData.params[vp.second.lenParam].name, "p" ) ) + " );\n"; } resizes.pop_back(); switch ( commandData.successCodes.size() ) { case 1: assert( commandData.successCodes[0] == "VK_SUCCESS" ); if ( commandData.errorCodes.empty() ) { std::string const callSequenceTemplate = R"(${dispatcher}${vkCommand}( ${firstCallArguments} ); ${resizes} ${dispatcher}${vkCommand}( ${secondCallArguments} ); )"; return replaceWithMap( callSequenceTemplate, { { "dispatcher", dispatcher }, { "firstCallArguments", firstCallArguments }, { "secondCallArguments", secondCallArguments }, { "resizes", resizes }, { "vkCommand", name } } ); } else { std::string const callSequenceTemplate = R"(Result result = static_cast( ${dispatcher}${vkCommand}( ${firstCallArguments} ) ); if ( result == Result::eSuccess ) { ${resizes} result = static_cast( ${dispatcher}${vkCommand}( ${secondCallArguments} ) ); } )"; return replaceWithMap( callSequenceTemplate, { { "dispatcher", dispatcher }, { "firstCallArguments", firstCallArguments }, { "secondCallArguments", secondCallArguments }, { "resizes", resizes }, { "vkCommand", name } } ); } case 2: case 3: { assert( ( commandData.successCodes[0] == "VK_SUCCESS" ) && ( commandData.successCodes[1] == "VK_INCOMPLETE" ) ); std::string const callSequenceTemplate = R"(Result result; do { result = static_cast( ${dispatcher}${vkCommand}( ${firstCallArguments} ) ); if ( ( result == Result::eSuccess ) && ${counterName} ) { ${resizes} result = static_cast( ${dispatcher}${vkCommand}( ${secondCallArguments} ) ); } } while ( result == Result::eIncomplete );)"; return replaceWithMap( callSequenceTemplate, { { "counterName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ) }, { "dispatcher", dispatcher }, { "firstCallArguments", firstCallArguments }, { "secondCallArguments", secondCallArguments }, { "resizes", resizes }, { "vkCommand", name } } ); } default: assert( false ); return ""; } } else { std::string resizeInstructions; if ( vectorParamIt->second.byStructure ) { std::string const resizeInstructionTemplate = R"(${vectorName}.resize( ${vectorSize} ); ${structName}.${pointerName} = ${vectorName}.data();)"; resizeInstructions = replaceWithMap( resizeInstructionTemplate, { { "pointerName", "p" + startUpperCase( vectorName ) }, { "structName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->first].name, "p" ) ) }, { "vectorName", vectorName }, { "vectorSize", vectorSize } } ); } else { std::string const resizeInstructionTemplate = R"(${vectorName}.resize( ${vectorSize} );)"; resizeInstructions = replaceWithMap( resizeInstructionTemplate, { { "vectorName", vectorName }, { "vectorSize", vectorSize } } ); } // no need to enumerate here, just two calls assert( commandData.returnType.type == "void" ); std::string const callSequenceTemplate = R"(${dispatcher}${vkCommand}( ${firstCallArguments} ); ${resizeInstructions} ${dispatcher}${vkCommand}( ${secondCallArguments} );)"; return replaceWithMap( callSequenceTemplate, { { "dispatcher", dispatcher }, { "firstCallArguments", firstCallArguments }, { "resizeInstructions", resizeInstructions }, { "secondCallArguments", secondCallArguments }, { "vkCommand", name } } ); } } else if ( ( commandData.returnType.type == "VkResult" ) && ( ( 1 < commandData.successCodes.size() ) || !commandData.errorCodes.empty() ) ) { std::string const callSequenceTemplate = R"(Result result = static_cast( ${dispatcher}${vkCommand}( ${callArguments} ) );)"; std::string callArguments = generateCallArgumentsEnhanced( commandData, initialSkipCount, false, singularParams, templatedParams, raii, raiiFactory, flavourFlags ); return replaceWithMap( callSequenceTemplate, { { "callArguments", callArguments }, { "dispatcher", dispatcher }, { "vkCommand", name } } ); } else { std::string const callSequenceTemplate = R"(${resultAssignment}${dispatcher}${vkCommand}( ${callArguments} );)"; std::string callArguments = generateCallArgumentsEnhanced( commandData, initialSkipCount, false, singularParams, templatedParams, raii, raiiFactory, flavourFlags ); std::string resultAssignment = generateResultAssignment( commandData ); return replaceWithMap( callSequenceTemplate, { { "callArguments", callArguments }, { "dispatcher", dispatcher }, { "resultAssignment", resultAssignment }, { "vkCommand", name } } ); } } std::string VulkanHppGenerator::generateCommand( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii ) const { std::string cmd; if ( name == "vkGetDescriptorEXT" ) { // special handling for this function: do not return the void*, but keep its argument as it is return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { 3 }, determineVectorParams( commandData.params ), false, { CommandFlavourFlagBits::noReturn | CommandFlavourFlagBits::keepVoidPtr, CommandFlavourFlagBits::singular }, raii, false, { CommandFlavourFlagBits::noReturn | CommandFlavourFlagBits::keepVoidPtr, CommandFlavourFlagBits::singular } ); } else if ( name == "vkGetDeviceFaultInfoEXT" ) { // can't generate an enhanced version for such a complex command! Just use the standard version return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { 1, 2 }, {}, false, {}, raii, false, {} ); } else if ( name == "vkGetMemoryHostPointerPropertiesEXT" ) { // special handling for this function: need to keep the void* argument as a void*! cmd = generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { 3 }, {}, false, { CommandFlavourFlagBits::enhanced | CommandFlavourFlagBits::keepVoidPtr }, raii, false, { CommandFlavourFlagBits::enhanced | CommandFlavourFlagBits::keepVoidPtr } ); } else { std::vector returnParams = determineReturnParams( commandData.params ); switch ( returnParams.size() ) { case 0: cmd = generateCommand0Returns( name, commandData, initialSkipCount, definition, raii ); break; case 1: cmd = generateCommand1Returns( name, commandData, initialSkipCount, definition, raii, returnParams[0] ); break; case 2: cmd = generateCommand2Returns( name, commandData, initialSkipCount, definition, raii, returnParams ); break; case 3: cmd = generateCommand3Returns( name, commandData, initialSkipCount, definition, raii, returnParams ); break; } } if ( cmd.empty() ) { throw std::runtime_error( "Never encountered a function like <" + name + "> !" ); } extendSpecialCommands( name, definition, raii, cmd ); return cmd; } std::string VulkanHppGenerator::generateCommand0Returns( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii ) const { if ( hasPointerParams( commandData.params ) ) { // command has pointer parameters return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, {}, determineVectorParams( commandData.params ), false, { CommandFlavourFlagBits::enhanced }, raii, false, { CommandFlavourFlagBits::enhanced } ); } else if ( commandData.returnType.type != "VkResult" ) { // command has no pointer parameters and does not return a VkResult return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, {}, {}, false, {}, raii, false, { CommandFlavourFlagBits::enhanced } ); } else { // command has no pointer parameters and returns a VkResult return generateCommandSetExclusive( name, commandData, initialSkipCount, definition, raii ); } } std::string VulkanHppGenerator::generateCommand1Returns( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, size_t returnParam ) const { std::map vectorParams = determineVectorParams( commandData.params ); if ( vectorParams.contains( returnParam ) ) { return generateCommand1ReturnsVector( name, commandData, initialSkipCount, definition, raii, returnParam, vectorParams ); } else { return generateCommand1ReturnsValue( name, commandData, initialSkipCount, definition, raii, returnParam, vectorParams ); } } std::string VulkanHppGenerator::generateCommand1ReturnsStruct( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, size_t returnParam, std::map const & vectorParams, std::map::const_iterator structIt ) const { if ( !structureHoldsHandle( structIt->second ) ) { // the returnType is a struct does not hold a handle if ( !structureHoldsVector( structIt->second ) ) { // the returnType is a struct does not hold a handle or a vector if ( structIt->second.extendedBy.empty() ) { // the returnType is a not extendable struct (without a handle or a vector) return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, false, { CommandFlavourFlagBits::enhanced }, raii, false, { CommandFlavourFlagBits::enhanced } ); } else { // the returnType is an extendable struct (without a handle or a vector) std::string const & returnType = commandData.params[returnParam].type.type; if ( !structureChainHoldsHandle( returnType ) ) { // there's no handle in the structure chain of the returnType struct std::string command; if ( raii && structureChainHoldsVector( returnType ) ) { // raii functions returning a structure chain that might hold a vector need to also have the standard implementation! command = generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::chained }, raii, false, {} ); } // no matter if there's a vector in some extending struct, generate some enhanced functions return command + generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::chained }, raii, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::chained } ); } } } } return ""; } std::string VulkanHppGenerator::generateCommand1ReturnsValue( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, size_t returnParam, std::map const & vectorParams ) const { std::string const & returnType = commandData.params[returnParam].type.type; if ( bool isHandle = isHandleType( returnType ); isHandle || m_baseTypes.contains( returnType ) || m_bitmasks.contains( returnType ) || m_enums.contains( returnType ) || m_externalTypes.contains( returnType ) ) { // the returnType is a handle or some basic type bool unique = isHandle && ( commandData.returnType.type == "VkResult" ); // only if the return type is VkResult, we need to have a unique version return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, unique, { CommandFlavourFlagBits::enhanced }, raii, isHandle, { CommandFlavourFlagBits::enhanced } ); } else if ( auto structIt = findByNameOrAlias( m_structs, returnType ); structIt != m_structs.end() ) { return generateCommand1ReturnsStruct( name, commandData, initialSkipCount, definition, raii, returnParam, vectorParams, structIt ); } return ""; } std::string VulkanHppGenerator::generateCommand1ReturnsVector( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, size_t returnParam, std::map const & vectorParams ) const { std::string const & returnType = commandData.params[returnParam].type.type; if ( returnType == "void" ) { // the retunType is a vector of void return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::singular }, raii, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::singular } ); } else if ( isHandleType( returnType ) ) { // the returnType is a vector of handles bool unique = ( commandData.returnType.type == "VkResult" ); // only if the return type is VkResult, we need to have a unique version std::vector flavourFlags = { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }; std::vector raiiFlavourFlags = { CommandFlavourFlagBits::enhanced }; if ( isLenByParam( commandData.params[returnParam].lenExpression, commandData.params ) ) { // the vector has a len by param -> also generate the singular version flavourFlags.push_back( CommandFlavourFlagBits::singular ); raiiFlavourFlags.push_back( CommandFlavourFlagBits::singular ); } return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, unique, flavourFlags, raii, true, raiiFlavourFlags ); } else if ( auto structIt = findByNameOrAlias( m_structs, returnType ); structIt != m_structs.end() ) { return generateCommand1ReturnsVectorOfStructs( name, commandData, initialSkipCount, definition, raii, returnParam, vectorParams, structIt ); } return ""; } std::string VulkanHppGenerator::generateCommand1ReturnsVectorOfStructs( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, size_t returnParam, std::map const & vectorParams, std::map::const_iterator structIt ) const { if ( auto handleMemberIt = findHandleMember( structIt->second.members ); handleMemberIt != structIt->second.members.end() ) { // the returnType is a vector of structs with a handle if ( auto vectorMemberIt = findVectorMember( structIt->second.members ); vectorMemberIt != structIt->second.members.end() ) { // the returnType is a vector of structs with a handle and a vector if ( structIt->second.extendedBy.empty() ) { // the returnType is a vector of non-extendable structs with a handle and a vector if ( handleMemberIt == vectorMemberIt ) { // the returnType is a vector of non-extendable structs with a vector of handles checkForError( std::find_if( std::next( vectorMemberIt ), structIt->second.members.end(), []( auto const & md ) { return !md.lenExpressions.empty(); } ) == structIt->second.members.end(), structIt->second.xmlLine, "Structure " + structIt->first + " used for vector of handles return has multiple members with lenExpressions!" ); return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, true, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }, raii, true, { CommandFlavourFlagBits::enhanced } ); } } } } else { // the returnType is a vector of structs without a handle if ( auto vectorMemberIt = findVectorMember( structIt->second.members ); vectorMemberIt != structIt->second.members.end() ) { // the returnType is a vector of structs with a vector // can't generate an enhanced version for such a complex command! Just use the standard version return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, { returnParam }, vectorParams, false, {}, raii, false, {} ); } } return ""; } std::string VulkanHppGenerator::generateCommand2Returns( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams ) const { assert( returnParams.size() == 2 ); std::map vectorParams = determineVectorParams( commandData.params ); if ( encodesEnumeration( commandData.params, vectorParams, returnParams[0], returnParams[1] ) ) { return generateCommand2ReturnsEnum( name, commandData, initialSkipCount, definition, raii, returnParams, vectorParams ); } else if ( bool param0IsVector = vectorParams.contains( returnParams[0] ), param1IsVector = vectorParams.contains( returnParams[1] ); param0IsVector && vectorParams.contains( returnParams[1] ) ) { // both parameters encode vectors return generateCommand2ReturnsVectorVector( name, commandData, initialSkipCount, definition, raii, returnParams, vectorParams ); } else if ( param0IsVector ) { // only the first parameter encodes a vector return generateCommand2ReturnsVectorValue( name, commandData, initialSkipCount, definition, raii, returnParams, vectorParams ); } else if ( param1IsVector ) { // only the second parameter encodes a vector } else { // no parameter encodes a vector return generateCommand2ReturnsValueValue( name, commandData, initialSkipCount, definition, raii, returnParams, vectorParams ); } return ""; } std::string VulkanHppGenerator::generateCommand2ReturnsEnum( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams, std::map const & vectorParams ) const { std::string const & returnType1 = commandData.params[returnParams[1]].type.type; if ( isHandleType( returnType1 ) ) { // the return params specify an enumaration of handles return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }, raii, name != "vkGetSwapchainImagesKHR", // don't make a raii factory for swapchain images! { CommandFlavourFlagBits::enhanced } ); } else if ( auto structIt = findByNameOrAlias( m_structs, returnType1 ); structIt != m_structs.end() ) { // the return params specify an enumeration of structures if ( structIt->second.extendedBy.empty() ) { // the return params specify an enumeration of not extendable structs // we don't care if that struct holds handles; it seems they are not created this way! if ( structureHoldsVector( structIt->second ) ) { // the return params specify an enumeration of not extendable structs with a vector!! // can't generate an enhanced version for such a complex command! Just use the standard version return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, {}, raii, true, {} ); } else { // the return params specify an enumeration of not extendable struct without vectors return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }, raii, false, { CommandFlavourFlagBits::enhanced } ); } } else { // the return params specify an enumeration of extendable structs if ( !structureChainHoldsHandle( returnType1 ) ) { // the return params specify an enumeration of structure chains without handles if ( !structureChainHoldsVector( returnType1 ) ) { // the return params specify an enumeration of structure chains without handles or vectors return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator, CommandFlavourFlagBits::chained, CommandFlavourFlagBits::chained | CommandFlavourFlagBits::withAllocator }, raii, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::chained } ); } } } } else if ( ( returnType1 == "void" ) || m_enums.contains( returnType1 ) ) { // the return params specify an enumeration of basic types or void return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }, raii, false, { CommandFlavourFlagBits::enhanced } ); } return ""; } std::string VulkanHppGenerator::generateCommand2ReturnsValueValue( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams, std::map const & vectorParams ) const { std::string const & returnType0 = commandData.params[returnParams[0]].type.type; std::string const & returnType1 = commandData.params[returnParams[1]].type.type; if ( auto structIt = findByNameOrAlias( m_structs, returnType0 ); structIt != m_structs.end() ) { // the first return param is a single struct if ( !structureHoldsHandle( structIt->second ) ) { // the first return param is a single struct without handles if ( !structureHoldsVector( structIt->second ) ) { // the first return param is a single struct without handles or vectors if ( !isStructureChainAnchor( returnType0 ) ) { // the first return param is a single, not extendable struct without handles or vectors if ( m_externalTypes.contains( returnType1 ) ) { // the second return param is just some basic type return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced }, raii, false, { CommandFlavourFlagBits::enhanced } ); } } } } } return ""; } std::string VulkanHppGenerator::generateCommand2ReturnsVectorValue( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams, std::map const & vectorParams ) const { if ( commandData.params[returnParams[0]].type.type == "uint64_t" ) { // the first return param is a vector of uint64_t if ( commandData.params[returnParams[1]].type.type == "uint64_t" ) { // the second return param is a uint64_t return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator, CommandFlavourFlagBits::singular }, raii, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::singular } ); } } else if ( auto structIt = findByNameOrAlias( m_structs, commandData.params[returnParams[0]].type.type ); structIt != m_structs.end() ) { // the first return param is a vector of structs if ( !structureHoldsHandle( structIt->second ) ) { // the first return param is a vector of structs without handles if ( structureHoldsVector( structIt->second ) ) { // the first return param is a vector of structs with vectors // can't generate an enhanced version for such a complex command, no matter if the struct is extendable or not, no matter what the second return // parameter is return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, {}, raii, true, {} ); } } } return ""; } std::string VulkanHppGenerator::generateCommand2ReturnsVectorVector( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams, std::map const & vectorParams ) const { if ( auto structIt = findByNameOrAlias( m_structs, commandData.params[returnParams[0]].type.type ); structIt != m_structs.end() ) { // the first return param is a vector of structs if ( !structureHoldsHandle( structIt->second ) ) { // the first return param is a vector of structs without handles if ( structureHoldsVector( structIt->second ) ) { // the first return param is a vector of structs with vectors // can't generate an enhanced version for such a complex command, no matter if the struct is extendable or not, no matter what the second return // parameter is return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, {}, raii, true, {} ); } } } return ""; } std::string VulkanHppGenerator::generateCommand3Returns( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams ) const { assert( returnParams.size() == 3 ); std::map vectorParams = determineVectorParams( commandData.params ); if ( encodesEnumeration( commandData.params, vectorParams, returnParams[0], returnParams[1] ) ) { if ( encodesEnumeration( commandData.params, vectorParams, returnParams[0], returnParams[2] ) ) { return generateCommand3ReturnsEnumEnum( name, commandData, initialSkipCount, definition, raii, returnParams, vectorParams ); } } else if ( !vectorParams.contains( returnParams[0] ) ) { if ( encodesEnumeration( commandData.params, vectorParams, returnParams[1], returnParams[2] ) ) { return generateCommand3ReturnsValueEnum( name, commandData, initialSkipCount, definition, raii, returnParams, vectorParams ); } } return ""; } std::string VulkanHppGenerator::generateCommand3ReturnsEnumEnum( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams, std::map const & vectorParams ) const { if ( auto structIt1 = findByNameOrAlias( m_structs, commandData.params[returnParams[1]].type.type ); structIt1 != m_structs.end() ) { // the first enumerated return param is a vector of structs if ( !structureHoldsHandle( structIt1->second ) ) { // the first enumerated return param is a vector of structs without handles if ( !structureHoldsVector( structIt1->second ) ) { // the first enumerated return param is a vector of structs without handles or vectors if ( structIt1->second.extendedBy.empty() ) { // the first enumerated return param is a vector of non-extendable structs without handles or vectors if ( auto structIt2 = findByNameOrAlias( m_structs, commandData.params[returnParams[2]].type.type ); structIt2 != m_structs.end() ) { if ( !structureHoldsHandle( structIt2->second ) ) { // the second enumerated return param is a vector of structs without handles if ( !structureHoldsVector( structIt2->second ) ) { // the second enumerated return param is a vector of structs without handles or vectors if ( structIt2->second.extendedBy.empty() ) { // the second enumerated return param is a vector of non-extendable structs without handles or vectors return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }, raii, false, { CommandFlavourFlagBits::enhanced } ); } } } } } } } } return ""; } std::string VulkanHppGenerator::generateCommand3ReturnsValueEnum( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii, std::vector const & returnParams, std::map const & vectorParams ) const { std::string const & returnType2 = commandData.params[returnParams[2]].type.type; if ( auto structIt0 = findByNameOrAlias( m_structs, commandData.params[returnParams[0]].type.type ); structIt0 != m_structs.end() ) { // the first return param is a struct if ( !structureHoldsHandle( structIt0->second ) ) { // the first return param is a struct without handles if ( !structureHoldsVector( structIt0->second ) ) { // the first return param is a struct without handles or vectors if ( structIt0->second.extendedBy.empty() ) { // the first return param is a non-extendable struct without handles or vectors if ( returnType2 == "void" ) { return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator }, raii, false, { CommandFlavourFlagBits::enhanced } ); } } else { // the first return param is an extendable struct without handles or vectors if ( returnType2 == "void" ) { return generateCommandSetInclusive( name, commandData, initialSkipCount, definition, returnParams, vectorParams, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::withAllocator, CommandFlavourFlagBits::chained, CommandFlavourFlagBits::chained | CommandFlavourFlagBits::withAllocator }, raii, false, { CommandFlavourFlagBits::enhanced, CommandFlavourFlagBits::chained } ); } } } } } return ""; } std::string VulkanHppGenerator::generateCommandDefinitions() const { const std::string commandDefinitionsTemplate = R"( //=========================== //=== COMMAND Definitions === //=========================== ${commandDefinitions} )"; std::string commandDefinitions; std::set listedCommands; // some commands are listed with more than one extension! for ( auto const & feature : m_features ) { commandDefinitions += generateCommandDefinitions( feature.requireData, listedCommands, feature.name ); } for ( auto const & extension : m_extensions ) { commandDefinitions += generateCommandDefinitions( extension.requireData, listedCommands, extension.name ); } return replaceWithMap( commandDefinitionsTemplate, { { "commandDefinitions", commandDefinitions } } ); } std::string VulkanHppGenerator::generateCommandDefinitions( std::vector const & requireData, std::set & listedCommands, std::string const & title ) const { std::string str; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( listedCommands.insert( command.name ).second ) { str += generateCommandDefinitions( command.name, commandData.second.handle ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateCommandDefinitions( std::string const & command, std::string const & handle ) const { auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); std::string str = "\n" + generateCommand( command, commandIt->second, handle.empty() ? 0 : 1, true, false ); // special handling for destroy functions, filter out alias functions std::string commandName = generateCommandName( command, commandIt->second.params, 1 ); if ( ( command == commandIt->first ) && ( ( ( command.substr( 2, 7 ) == "Destroy" ) && ( commandName != "destroy" ) ) || ( command.substr( 2, 4 ) == "Free" ) || ( command == "vkReleasePerformanceConfigurationINTEL" ) ) ) { CommandData specialCommandData = commandIt->second; assert( ( 1 < specialCommandData.params.size() ) && ( specialCommandData.params[0].type.type == handle ) ); specialCommandData.params[1].optional = false; // make sure, the object to destroy/free/release is not optional in the shortened version! std::string destroyCommandString = generateCommand( command, specialCommandData, handle.empty() ? 0 : 1, true, false ); std::string shortenedName; if ( command.substr( 2, 7 ) == "Destroy" ) { shortenedName = "destroy"; } else if ( command.substr( 2, 4 ) == "Free" ) { shortenedName = "free"; } else { assert( command == "vkReleasePerformanceConfigurationINTEL" ); shortenedName = "release"; } size_t pos = destroyCommandString.find( commandName ); while ( pos != std::string::npos ) { destroyCommandString.replace( pos, commandName.length(), shortenedName ); pos = destroyCommandString.find( commandName, pos ); } // special handling for "free", to prevent interfering with MSVC debug free! if ( shortenedName == "free" ) { std::string toEncloseString = stripPrefix( handle, "Vk" ) + "::free"; std::string enclosedString = "( " + toEncloseString + " )"; pos = destroyCommandString.find( toEncloseString ); while ( pos != std::string::npos ) { destroyCommandString.replace( pos, toEncloseString.length(), enclosedString ); pos = destroyCommandString.find( toEncloseString, pos + enclosedString.length() ); } } // we need to remove the default argument for the first argument, to prevent ambiguities! assert( 1 < commandIt->second.params.size() ); pos = destroyCommandString.find( commandIt->second.params[1].name ); // skip the standard version of the function assert( pos != std::string::npos ); pos = destroyCommandString.find( commandIt->second.params[1].name, pos + 1 ); // get the argument to destroy in the advanced version assert( pos != std::string::npos ); pos = destroyCommandString.find( " VULKAN_HPP_DEFAULT_ASSIGNMENT( {} )", pos ); if ( pos != std::string::npos ) { destroyCommandString.erase( pos, strlen( " VULKAN_HPP_DEFAULT_ASSIGNMENT( {} )" ) ); } str += "\n" + destroyCommandString; } return str; } std::string VulkanHppGenerator::generateCommandEnhanced( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, std::map const & vectorParams, std::vector const & returnParams, CommandFlavourFlags flavourFlags ) const { const bool chained = flavourFlags & CommandFlavourFlagBits::chained; const bool singular = flavourFlags & CommandFlavourFlagBits::singular; assert( vectorParams.empty() || ( vectorParams.begin()->second.lenParam != INVALID_INDEX ) ); assert( !singular || !returnParams.empty() ); // if singular is true, then there is at least one returnParam ! std::set skippedParams = determineSkippedParams( commandData.params, initialSkipCount, vectorParams, returnParams, singular ); std::set templatedParams = ( flavourFlags & CommandFlavourFlagBits::keepVoidPtr ) ? std::set() : determineVoidPointerParams( commandData.params ); std::vector chainedReturnParams; if ( chained ) { chainedReturnParams = determineChainedReturnParams( commandData.params, returnParams ); assert( chainedReturnParams.size() == 1 ); } std::set singularParams = singular ? determineSingularParams( returnParams[0], vectorParams ) : std::set(); std::pair>> vectorSizeCheck = needsVectorSizeCheck( commandData.params, vectorParams, returnParams, singularParams, skippedParams ); const bool enumerating = determineEnumeration( vectorParams, returnParams ); std::vector dataTypes = determineDataTypes( commandData.params, vectorParams, returnParams, templatedParams, false ); std::string returnDataType = generateReturnDataType( vectorParams, returnParams, enumerating, dataTypes, flavourFlags, false ); std::string argumentTemplates = generateArgumentTemplates( commandData.params, returnParams, vectorParams, templatedParams, chainedReturnParams, false ); auto [allocatorTemplates, uniqueHandleAllocatorTemplates] = generateAllocatorTemplates( returnParams, dataTypes, vectorParams, chainedReturnParams, flavourFlags, definition ); std::string typenameCheck = generateTypenameCheck( returnParams, vectorParams, chainedReturnParams, definition, dataTypes, flavourFlags ); std::string nodiscard = generateNoDiscard( !returnParams.empty(), 1 < commandData.successCodes.size(), 1 < commandData.errorCodes.size() ); std::string returnType = generateReturnType( returnParams, vectorParams, flavourFlags, false, dataTypes ); std::string decoratedReturnType = generateDecoratedReturnType( commandData, returnParams, vectorParams, enumerating, flavourFlags, returnType ); std::string className = initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : ""; std::string classSeparator = commandData.handle.empty() ? "" : "::"; std::string commandName = generateCommandName( name, commandData.params, initialSkipCount, flavourFlags ); std::string argumentList = generateArgumentListEnhanced( commandData.params, returnParams, vectorParams, skippedParams, singularParams, templatedParams, chainedReturnParams, false, definition, flavourFlags, true ); std::string constString = commandData.handle.empty() ? "" : " const"; std::string noexceptString = generateNoExcept( commandData.errorCodes, returnParams, vectorParams, flavourFlags, vectorSizeCheck.first, false ); if ( definition ) { std::string vectorSizeCheckString = vectorSizeCheck.first ? generateVectorSizeCheck( name, commandData, initialSkipCount, vectorSizeCheck.second, skippedParams, false ) : ""; std::string returnVariable = generateReturnVariable( commandData, returnParams, vectorParams, flavourFlags ); std::string dataDeclarations = generateDataDeclarations( commandData, returnParams, vectorParams, templatedParams, flavourFlags, false, dataTypes, returnDataType, returnType, returnVariable ); std::string dataPreparation = generateDataPreparation( commandData, initialSkipCount, returnParams, vectorParams, templatedParams, flavourFlags, enumerating, dataTypes ); std::string dataSizeChecks = generateDataSizeChecks( commandData, returnParams, dataTypes, vectorParams, templatedParams, singular ); std::string callSequence = generateCallSequence( name, commandData, returnParams, vectorParams, initialSkipCount, singularParams, templatedParams, chainedReturnParams, flavourFlags, false, false ); std::string resultCheck = generateResultCheck( commandData, className, classSeparator, commandName, enumerating, ( returnParams.size() == 1 ) && ( commandData.params[returnParams[0]].type.type == "VkPipeline" ) && !singular, false ); std::string returnStatement = generateReturnStatement( name, commandData, returnVariable, returnType, decoratedReturnType, returnDataType, initialSkipCount, returnParams.empty() ? INVALID_INDEX : returnParams[0], flavourFlags, enumerating, false ); std::string const functionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html template <${argumentTemplates}${allocatorTemplates}typename Dispatch${uniqueHandleAllocatorTemplates}${typenameCheck}, typename std::enable_if::type> ${nodiscard}VULKAN_HPP_INLINE ${decoratedReturnType} ${className}${classSeparator}${commandName}( ${argumentList} )${const}${noexcept} { VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); #if (VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 ) ${functionPointerCheck} #endif ${vectorSizeCheck} ${dataSizeChecks} ${dataDeclarations} ${callSequence} ${resultCheck} ${dataPreparation} ${returnStatement} })"; return replaceWithMap( functionTemplate, { { "allocatorTemplates", allocatorTemplates }, { "argumentList", argumentList }, { "argumentTemplates", argumentTemplates }, { "callSequence", callSequence }, { "className", className }, { "classSeparator", classSeparator }, { "commandName", commandName }, { "const", constString }, { "dataDeclarations", dataDeclarations }, { "dataPreparation", dataPreparation }, { "dataSizeChecks", dataSizeChecks }, { "decoratedReturnType", decoratedReturnType }, { "functionPointerCheck", generateFunctionPointerCheck( name, commandData.requiredBy, false ) }, { "nodiscard", nodiscard }, { "noexcept", noexceptString }, { "resultCheck", resultCheck }, { "returnStatement", returnStatement }, { "typenameCheck", typenameCheck }, { "uniqueHandleAllocatorTemplates", uniqueHandleAllocatorTemplates }, { "vectorSizeCheck", vectorSizeCheckString }, { "vkCommandName", name } } ); } else { std::string const functionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html template <${argumentTemplates}${allocatorTemplates}typename Dispatch = VULKAN_HPP_DEFAULT_DISPATCHER_TYPE${uniqueHandleAllocatorTemplates}${typenameCheck}, typename std::enable_if::type = true> ${nodiscard}${decoratedReturnType} ${commandName}( ${argumentList} )${const}${noexcept};)"; return replaceWithMap( functionTemplate, { { "allocatorTemplates", allocatorTemplates }, { "argumentList", argumentList }, { "argumentTemplates", argumentTemplates }, { "commandName", commandName }, { "const", commandData.handle.empty() ? "" : " const" }, { "decoratedReturnType", decoratedReturnType }, { "nodiscard", nodiscard }, { "noexcept", noexceptString }, { "typenameCheck", typenameCheck }, { "uniqueHandleAllocatorTemplates", uniqueHandleAllocatorTemplates }, { "vkCommandName", name } } ); } } std::string VulkanHppGenerator::generateCommandName( std::string const & vulkanCommandName, std::vector const & params, size_t initialSkipCount, CommandFlavourFlags flavourFlags ) const { std::string commandName( startLowerCase( stripPrefix( vulkanCommandName, "vk" ) ) ); for ( size_t i = initialSkipCount - 1; i < initialSkipCount; --i ) // count down to zero, then wrap around and stop { std::string const & argumentType = params[i].type.type; std::string searchName = stripPrefix( argumentType, "Vk" ); std::string argumentTag = findTag( argumentType ); if ( !argumentTag.empty() ) { searchName = stripPostfix( searchName, argumentTag ); } size_t pos = commandName.find( searchName ); if ( pos == std::string::npos ) { searchName = startLowerCase( searchName ); pos = commandName.find( searchName ); } if ( pos != std::string::npos ) { size_t len = searchName.length(); if ( commandName.find( searchName + "s" ) == pos ) { // filter out any plural of the searchName as well! ++len; } commandName.erase( pos, len ); } else if ( ( searchName == "commandBuffer" ) && commandName.starts_with( "cmd" ) ) { commandName.erase( 0, 3 ); pos = 0; } if ( pos == 0 ) { commandName = startLowerCase( commandName ); } std::string commandTag = findTag( commandName ); if ( !argumentTag.empty() && ( argumentTag == commandTag ) ) { commandName = stripPostfix( commandName, argumentTag ); } } if ( flavourFlags & CommandFlavourFlagBits::singular ) { commandName = stripPluralS( commandName ); } if ( flavourFlags & CommandFlavourFlagBits::unique ) { commandName += "Unique"; } return commandName; } std::string VulkanHppGenerator::generateCommandSet( bool definition, std::string const & standard, std::vector const & enhanced, std::vector const & unique ) const { assert( unique.empty() || ( enhanced.size() == unique.size() ) ); std::string commandSet = "\n" + standard; if ( !enhanced.empty() ) { std::string separator = definition ? "\n" : ""; commandSet += separator + "\n#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE\n"; bool firstEnhanced = true; for ( auto const & e : enhanced ) { if ( !firstEnhanced ) { commandSet += separator + "\n"; } firstEnhanced = false; commandSet += e; } if ( !unique.empty() ) { commandSet += separator + "\n# ifndef VULKAN_HPP_NO_SMART_HANDLE\n"; bool firstUnique = true; for ( auto const & u : unique ) { if ( !firstUnique ) { commandSet += separator + "\n"; } firstUnique = false; commandSet += u; } commandSet += "\n# endif /* VULKAN_HPP_NO_SMART_HANDLE */"; } commandSet += "\n#endif /* VULKAN_HPP_DISABLE_ENHANCED_MODE */"; } commandSet += "\n"; return commandSet; } std::string VulkanHppGenerator::generateCommandSet( std::string const & standard, std::string const & enhanced ) const { const std::string commandTemplate = R"( #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE ${commandStandard} #else ${commandEnhanced} #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; return replaceWithMap( commandTemplate, std::map( { { "commandEnhanced", enhanced }, { "commandStandard", standard } } ) ); } std::string VulkanHppGenerator::generateCommandSetExclusive( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii ) const { if ( raii ) { return generateRAIIHandleCommandEnhanced( name, commandData, initialSkipCount, {}, {}, definition ); } else { return generateCommandSet( generateCommandStandard( name, commandData, initialSkipCount, definition, raii ), generateCommandEnhanced( name, commandData, initialSkipCount, definition, {}, {}, {} ) ); } } std::string VulkanHppGenerator::generateCommandSetInclusive( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, std::vector returnParams, std::map vectorParams, bool unique, std::vector const & flags, bool raii, bool raiiFactory, std::vector const & raiiFlags ) const { static const std::vector emptyReturnParams; static const std::map emptyVectorParams; if ( raii ) { std::string raiiCommands; if ( raiiFlags.empty() ) { // Functions without enhancements need to have a standard implementation raiiCommands = generateRAIIHandleCommandStandard( name, commandData, initialSkipCount, definition, raii ); } else { for ( auto flag : raiiFlags ) { const bool noReturn = flag & CommandFlavourFlagBits::noReturn; assert( !noReturn || !raiiFactory ); // noReturn => !raiiFactory raiiCommands += raiiFactory ? generateRAIIHandleCommandFactory( name, commandData, initialSkipCount, returnParams, vectorParams, definition, flag ) : generateRAIIHandleCommandEnhanced( name, commandData, initialSkipCount, noReturn ? emptyReturnParams : returnParams, noReturn ? emptyVectorParams : vectorParams, definition, flag ); } } return raiiCommands; } else { std::vector enhancedCommands, uniqueCommands; for ( auto flag : flags ) { const bool noReturn = flag & CommandFlavourFlagBits::noReturn; assert( !noReturn || !unique ); // noReturn => !unique enhancedCommands.push_back( generateCommandEnhanced( name, commandData, initialSkipCount, definition, noReturn ? emptyVectorParams : vectorParams, noReturn ? emptyReturnParams : returnParams, flag ) ); if ( unique ) { uniqueCommands.push_back( generateCommandEnhanced( name, commandData, initialSkipCount, definition, vectorParams, returnParams, flag | CommandFlavourFlagBits::unique ) ); } } return generateCommandSet( definition, generateCommandStandard( name, commandData, initialSkipCount, definition, raii ), enhancedCommands, uniqueCommands ); } } std::string VulkanHppGenerator::generateCommandStandard( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii ) const { std::set skippedParams = determineSkippedParams( commandData.params, initialSkipCount, {}, {}, false ); std::string argumentList = generateArgumentListStandard( commandData.params, skippedParams, definition, raii, true ); std::string commandName = generateCommandName( name, commandData.params, initialSkipCount ); std::string nodiscard = ( 1 < commandData.successCodes.size() + commandData.errorCodes.size() ) ? "VULKAN_HPP_NODISCARD " : ""; std::string returnType = stripPrefix( commandData.returnType.type, "Vk" ); if ( definition ) { std::string functionBody = "d." + name + "( " + generateCallArgumentsStandard( commandData.params, initialSkipCount ) + " )"; if ( commandData.returnType.type.starts_with( "Vk" ) ) { functionBody = "return static_cast<" + returnType + ">( " + functionBody + " )"; } else if ( commandData.returnType.type != "void" ) { functionBody = "return " + functionBody; } std::string const functionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html template ::type> ${nodiscard}VULKAN_HPP_INLINE ${returnType} ${className}${classSeparator}${commandName}( ${argumentList} )${const} VULKAN_HPP_NOEXCEPT { VULKAN_HPP_ASSERT( d.getVkHeaderVersion() == VK_HEADER_VERSION ); ${functionBody}; })"; return replaceWithMap( functionTemplate, { { "argumentList", argumentList }, { "className", initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : "" }, { "classSeparator", commandData.handle.empty() ? "" : "::" }, { "commandName", commandName }, { "const", commandData.handle.empty() ? "" : " const" }, { "functionBody", functionBody }, { "nodiscard", nodiscard }, { "returnType", returnType }, { "vkCommandName", name } } ); } else { std::string const functionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html template ::type = true> ${nodiscard}${returnType} ${commandName}( ${argumentList} VULKAN_HPP_DEFAULT_DISPATCHER_ASSIGNMENT )${const} VULKAN_HPP_NOEXCEPT;)"; return replaceWithMap( functionTemplate, { { "argumentList", argumentList }, { "commandName", commandName }, { "const", commandData.handle.empty() ? "" : " const" }, { "nodiscard", nodiscard }, { "returnType", returnType }, { "vkCommandName", name } } ); } } std::string VulkanHppGenerator::generateConstexprString( std::pair const & structData ) const { // structs with a VkBaseInStructure and VkBaseOutStructure can't be a constexpr! const bool isConstExpression = ( structData.first != "VkBaseInStructure" ) && ( structData.first != "VkBaseOutStructure" ); return isConstExpression ? ( std::string( "VULKAN_HPP_CONSTEXPR" ) + ( containsDeprecated( structData.second.members ) ? "_17 " : ( ( containsUnion( structData.first ) || containsArray( structData.first ) || containsDeprecated( structData.second.members ) ) ? "_14 " : " " ) ) ) : ""; } std::string VulkanHppGenerator::generateConstexprDefines() const { auto const constexprFunctionTemplate = std::string{ R"( template ::value>::type> ${deprecated}VULKAN_HPP_CONSTEXPR uint32_t ${constName}( ${arguments} ) { return ${implementation}; } )" }; auto const constexprCallTemplate = std::string{ R"( ${deprecated}VULKAN_HPP_CONSTEXPR_INLINE auto ${constName} = ${callee}( ${arguments} ); )" }; auto const constexprValueTemplate = std::string{ R"( ${deprecated}VULKAN_HPP_CONSTEXPR_INLINE ${type} ${constName} = ${value}; )" }; auto const deprecatedAttribute = std::string{ R"(VULKAN_HPP_DEPRECATED("${reason}") )" }; // handle the value and callee macros first so they are visible for use in functions below. auto constexprDefines = std::string{ R"( //=========================== //=== CONSTEXPR CONSTANTs === //=========================== )" }; { auto const generateConstantsAndProtection = [&constexprValueTemplate, this]( std::vector const & requireData, std::string const & title, std::set & listedConstants ) { std::string constants; forEachRequiredConstant( requireData, listedConstants, [&constants, &constexprValueTemplate, this]( std::pair const & constantData ) { constants += replaceWithMap( constexprValueTemplate, { { "type", constantData.second.type }, { "constName", generateTaggedCamelCase( constantData.first ) }, { "deprecated", "" }, { "value", constantData.first } } ); } ); return addTitleAndProtection( title, constants ); }; std::set listedConstants; for ( auto const & feature : m_features ) { constexprDefines += generateConstantsAndProtection( feature.requireData, feature.name, listedConstants ); } for ( auto const & extension : m_extensions ) { constexprDefines += generateConstantsAndProtection( extension.requireData, extension.name, listedConstants ); } } // values constexprDefines += R"( //======================== //=== CONSTEXPR VALUEs === //======================== )"; for ( auto const & [macro, data] : m_definesPartition.values ) { auto const deprecated = data.deprecated ? replaceWithMap( deprecatedAttribute, { { "reason", data.deprecationReason } } ) : ""; // make `macro` PascalCase and strip the `Vk` prefix auto const constName = stripPrefix( toCamelCase( macro ), "Vk" ); auto const valueString = replaceWithMap( constexprValueTemplate, { { "type", "uint32_t" }, { "constName", constName }, { "deprecated", deprecated }, { "value", macro } } ); constexprDefines += valueString; } // functions constexprDefines += R"( //========================= //=== CONSTEXPR CALLEEs === //========================= )"; for ( auto const & [macro, data] : m_definesPartition.callees ) { auto const deprecated = data.deprecated ? replaceWithMap( deprecatedAttribute, { { "reason", data.deprecationReason } } ) : ""; // make `macro` camelCase and strip the `Vk` prefix auto const constName = startLowerCase( stripPrefix( toCamelCase( macro ), "Vk" ) ); // for every parameter, need to use auto const and append a comma if needed (i.e. has more than one parameter, and not for the last one) auto parametersString = std::string{}; for ( auto const & paramString : data.params ) { parametersString += "T const " + paramString + ", "; } // trim the last two characters (i.e. the last comma and the space) parametersString.resize( parametersString.size() - 2 ); auto const functionString = replaceWithMap( constexprFunctionTemplate, { { "arguments", parametersString }, { "constName", constName }, { "deprecated", deprecated }, { "implementation", data.possibleDefinition } } ); constexprDefines += functionString; } // callers constexprDefines += R"( //========================= //=== CONSTEXPR CALLERs === //========================= )"; for ( auto const & [macro, data] : m_definesPartition.callers ) { auto const deprecated = data.deprecated ? replaceWithMap( deprecatedAttribute, { { "reason", data.deprecationReason } } ) : ""; // make `macro` PascalCase and strip the `Vk` prefix auto const constName = stripPrefix( toCamelCase( macro ), "Vk" ); auto argumentsString = std::string{}; // for every argument, append a comma if needed (i.e. has more than one parameter, and not for the last one) for ( auto const & argString : data.params ) { argumentsString += argString + ", "; } // trim the last two characters (i.e. the last comma and the space) argumentsString.resize( argumentsString.size() - 2 ); auto const callerString = replaceWithMap( constexprCallTemplate, { { "arguments", argumentsString }, { "callee", startLowerCase( data.possibleCallee ) }, { "constName", constName }, { "deprecated", deprecated } } ); constexprDefines += callerString; } // extension constexprs auto const extensionConstexprDefinesTemplate = std::string{ R"( //================================= //=== CONSTEXPR EXTENSION NAMEs === //================================= ${enumConstants} )" }; std::string enumConstants; for ( auto const & extension : m_extensions ) { std::string enumConstantsByExtension; std::set encounteredEnumConstants; forEachRequiredEnumConstant( extension.requireData, encounteredEnumConstants, [&enumConstantsByExtension, &extension, this]( EnumConstantData const & enumConstant ) { std::string constant = generateTaggedCamelCase( enumConstant.name ); std::string deprecationMessage; if ( extension.isDeprecated ) { assert( extension.obsoletedBy.empty() && extension.promotedTo.empty() ); deprecationMessage = extension.deprecatedBy.empty() ? ( "The " + extension.name + " extension has been deprecated." ) : ( "The " + extension.name + " extension has been deprecated by " + extension.deprecatedBy + "." ); } else if ( !extension.obsoletedBy.empty() ) { assert( extension.promotedTo.empty() ); deprecationMessage = "The " + extension.name + " extension has been obsoleted by " + extension.obsoletedBy + "."; } else if ( !extension.promotedTo.empty() ) { // promoted extensions are _not_ deprecated! } auto const deprecatedPrefix = deprecationMessage.empty() ? "" : "VULKAN_HPP_DEPRECATED( \"" + deprecationMessage + "\" )"; enumConstantsByExtension += deprecatedPrefix + "VULKAN_HPP_CONSTEXPR_INLINE auto " + constant + " = " + enumConstant.name + ";\n"; } ); enumConstants += addTitleAndProtection( extension.name, enumConstantsByExtension ); } constexprDefines += replaceWithMap( extensionConstexprDefinesTemplate, { { "enumConstants", enumConstants } } ); return constexprDefines; } std::string VulkanHppGenerator::generateConstexprUsings() const { auto constexprUsings = std::string{ R"( //=========================== //=== CONSTEXPR CONSTANTs === //=========================== )" }; auto const constexprUsingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${constName}; )" }; auto const pascalCasePrefixStrip = []( std::string const & macro ) { return stripPrefix( toCamelCase( macro ), "Vk" ); }; auto const camelCasePrefixStrip = []( std::string const & macro ) { return startLowerCase( stripPrefix( toCamelCase( macro ), "Vk" ) ); }; // constants { auto const generateConstantsAndProtection = [&]( std::vector const & requireData, std::string const & title, std::set & listedConstants ) { std::string constants; forEachRequiredEnumConstant( requireData, listedConstants, [&constants, this]( EnumConstantData const & enumConstant ) { constants += " using VULKAN_HPP_NAMESPACE::" + generateTaggedCamelCase( enumConstant.name ) + ";\n"; } ); forEachRequiredConstant( requireData, listedConstants, [&constants, this]( std::pair const & constantData ) { constants += " using VULKAN_HPP_NAMESPACE::" + generateTaggedCamelCase( constantData.first ) + ";\n"; } ); return addTitleAndProtection( title, constants ); }; std::set listedConstants; for ( auto const & feature : m_features ) { constexprUsings += generateConstantsAndProtection( feature.requireData, feature.name, listedConstants ); } for ( auto const & extension : m_extensions ) { constexprUsings += generateConstantsAndProtection( extension.requireData, extension.name, listedConstants ); } } // values constexprUsings += R"( //======================== //=== CONSTEXPR VALUEs === //======================== )"; for ( auto const & macro : m_definesPartition.values ) { // make `macro` PascalCase and strip the `Vk` prefix auto const constName = pascalCasePrefixStrip( macro.first ); constexprUsings += replaceWithMap( constexprUsingTemplate, { { "constName", constName } } ); } // callees constexprUsings += R"( //========================= //=== CONSTEXPR CALLEEs === //========================= )"; for ( auto const & macro : m_definesPartition.callees ) { // make `macro` camelCase and strip the `Vk` prefix auto const constName = camelCasePrefixStrip( macro.first ); constexprUsings += replaceWithMap( constexprUsingTemplate, { { "constName", constName } } ); } // callers constexprUsings += R"( //========================== //=== CONSTEXPR CALLERs === //========================== )"; for ( auto const & macro : m_definesPartition.callers ) { // make `macro` PascalCase and strip the `Vk` prefix auto const constName = pascalCasePrefixStrip( macro.first ); constexprUsings += replaceWithMap( constexprUsingTemplate, { { "constName", constName } } ); } return constexprUsings; } std::string VulkanHppGenerator::generateCppModuleFuncpointerUsings() const { std::string funcpointerUsings = R"( //==================== //=== FUNCPOINTERs === //==================== )"; auto const generateUsingsAndProtection = [this]( std::vector const & requireData, std::string const & title ) { std::string usings; forEachRequiredFuncPointer( requireData, [&usings]( std::pair const & funcPointerData ) { usings += " using VULKAN_HPP_NAMESPACE::PFN_" + stripPrefix( funcPointerData.first, "PFN_vk" ) + ";\n"; } ); return addTitleAndProtection( title, usings ); }; for ( auto const & feature : m_features ) { funcpointerUsings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { funcpointerUsings += generateUsingsAndProtection( extension.requireData, extension.name ); } return funcpointerUsings; } std::string VulkanHppGenerator::generateCppModuleHandleUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${className}; )" }; auto handleUsings = std::string{ R"( //=============== //=== HANDLEs === //=============== using VULKAN_HPP_NAMESPACE::isVulkanHandleType; )" }; auto const generateUsingsAndProtection = [&usingTemplate, this]( std::vector const & requireData, std::string const & title ) { std::string usings; forEachRequiredHandle( requireData, [&usings, &usingTemplate]( std::pair const & handleData ) { usings += replaceWithMap( usingTemplate, { { "className", stripPrefix( handleData.first, "Vk" ) } } ); } ); return addTitleAndProtection( title, usings ); }; for ( auto const & feature : m_features ) { handleUsings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { handleUsings += generateUsingsAndProtection( extension.requireData, extension.name ); } return handleUsings; } std::string VulkanHppGenerator::generateCppModuleStructUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${structName}; )" }; auto structUsings = std::string{ R"( //=============== //=== STRUCTs === //=============== )" }; auto listedStructs = std::set{}; auto const generateUsingsAndProtection = [&listedStructs, &usingTemplate, this]( std::vector const & requireData, std::string const & title ) { std::string localUsings; forEachRequiredStruct( requireData, [&localUsings, &usingTemplate, &listedStructs]( std::pair const & structData ) { if ( listedStructs.insert( structData.first ).second ) { localUsings += replaceWithMap( usingTemplate, { { "structName", stripPrefix( structData.first, "Vk" ) } } ); for ( auto const & alias : structData.second.aliases ) { localUsings += replaceWithMap( usingTemplate, { { "structName", stripPrefix( alias.first, "Vk" ) } } ); } } } ); return addTitleAndProtection( title, localUsings ); }; for ( auto const & feature : m_features ) { structUsings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { structUsings += generateUsingsAndProtection( extension.requireData, extension.name ); } return structUsings; } std::string VulkanHppGenerator::generateCppModuleSharedHandleUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::Shared${handleName}; )" }; auto sharedHandleUsings = std::string{ R"( //====================== //=== SHARED HANDLEs === //====================== )" }; auto const [smartHandleEnter, smartHandleLeave] = generateProtection( "VULKAN_HPP_NO_SMART_HANDLE", false ); sharedHandleUsings += "\n" + smartHandleEnter; auto const generateUsingsAndProtection = [&usingTemplate, this]( std::vector const & requireData, std::string const & title ) { std::string usings; forEachRequiredHandle( requireData, [&usings, &usingTemplate]( std::pair const & handleData ) { usings += replaceWithMap( usingTemplate, { { "handleName", stripPrefix( handleData.first, "Vk" ) } } ); } ); return addTitleAndProtection( title, usings ); }; for ( auto const & feature : m_features ) { sharedHandleUsings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { sharedHandleUsings += generateUsingsAndProtection( extension.requireData, extension.name ); } sharedHandleUsings += R"( using VULKAN_HPP_NAMESPACE::SharedHandleTraits; )"; sharedHandleUsings += R"( //=== VK_KHR_swapchain enum === using VULKAN_HPP_NAMESPACE::SwapchainOwns; )"; sharedHandleUsings += smartHandleLeave + "\n"; return sharedHandleUsings; } std::string VulkanHppGenerator::generateCppModuleUniqueHandleUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::Unique${handleName}; )" }; auto uniqueHandleUsings = std::string{ R"( //====================== //=== UNIQUE HANDLEs === //====================== )" }; auto const [smartHandleEnter, smartHandleLeave] = generateProtection( "VULKAN_HPP_NO_SMART_HANDLE", false ); uniqueHandleUsings += "\n" + smartHandleEnter; auto const generateUsingsAndProtection = [&usingTemplate, this]( std::vector const & requireData, std::string const & title ) { std::string usings; forEachRequiredHandle( requireData, [&usings, &usingTemplate]( std::pair const & handleData ) { if ( !handleData.second.deleteCommand.empty() ) { usings += replaceWithMap( usingTemplate, { { "handleName", stripPrefix( handleData.first, "Vk" ) } } ); } } ); return addTitleAndProtection( title, usings ); }; for ( auto const & feature : m_features ) { uniqueHandleUsings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { uniqueHandleUsings += generateUsingsAndProtection( extension.requireData, extension.name ); } uniqueHandleUsings += R"( using VULKAN_HPP_NAMESPACE::UniqueHandleTraits; )"; uniqueHandleUsings += smartHandleLeave + "\n"; return uniqueHandleUsings; } std::string VulkanHppGenerator::generateCppModuleFuncsUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${funcName}; )" }; auto funcUsings = std::string{ R"( //=========================== //=== COMMAND Definitions === //=========================== )" }; for ( auto const & func : m_handles.at( "" ).commands ) { funcUsings += replaceWithMap( usingTemplate, { { "funcName", startLowerCase( stripPrefix( func, "vk" ) ) } } ); } auto const [enter, leave] = generateProtection( "VULKAN_HPP_NO_SMART_HANDLE", false ); funcUsings += "\n" + enter + replaceWithMap( usingTemplate, { { "funcName", "createInstanceUnique" } } ) + leave + "\n"; return funcUsings; } std::string VulkanHppGenerator::generateCppModuleEnumUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${enumName}; )" }; auto enumUsings = std::string{ R"( //============= //=== ENUMs === //============= )" }; auto listedEnums = std::set{}; // insert CppType first enumUsings += replaceWithMap( usingTemplate, { { "enumName", "CppType" } } ); auto const generateUsingsAndProtection = [&listedEnums, &usingTemplate, this]( std::vector const & requireData, std::string const & title ) { auto localUsings = std::string{}; for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { if ( auto const & enumIt = m_enums.find( type.name ); enumIt != m_enums.end() && listedEnums.insert( type.name ).second ) { auto const enumName = stripPrefix( enumIt->first, "Vk" ); localUsings += replaceWithMap( usingTemplate, { { "enumName", enumName } } ); for ( auto const & alias : enumIt->second.aliases ) { localUsings += replaceWithMap( usingTemplate, { { "enumName", stripPrefix( alias.first, "Vk" ) } } ); } if ( auto const bitmaskIt = std::ranges::find_if( m_bitmasks, [&enumIt]( std::pair const & bitmask ) { return bitmask.second.require == enumIt->first; } ); bitmaskIt != m_bitmasks.end() ) { localUsings += replaceWithMap( usingTemplate, { { "enumName", stripPrefix( bitmaskIt->first, "Vk" ) } } ); for ( auto const & alias : bitmaskIt->second.aliases ) { localUsings += replaceWithMap( usingTemplate, { { "enumName", stripPrefix( alias.first, "Vk" ) } } ); } } } } } return addTitleAndProtection( title, localUsings ); }; for ( auto const & feature : m_features ) { enumUsings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { enumUsings += generateUsingsAndProtection( extension.requireData, extension.name ); } // finally insert IndexTypeValue auto const indexTypeComment = R"( //========================= //=== Index Type Traits === //========================= )"; enumUsings += indexTypeComment + replaceWithMap( usingTemplate, { { "enumName", "IndexTypeValue" } } ); return enumUsings; } std::string VulkanHppGenerator::generateCppModuleFormatTraitsUsings() const { // everything is hardcoded, so things are very easy... auto formatTraitsUsings = std::string{ R"( //===================== //=== Format Traits === //===================== )" }; auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${function}; )" }; auto const formatTraitFunctions = std::array{ "blockExtent", "blockSize", "compatibilityClass", "componentBits", "componentCount", "componentName", "componentNumericFormat", "componentPlaneIndex", "componentsAreCompressed", "compressionScheme", "getDepthFormats", "getDepthStencilFormats", "getStencilFormats", "hasDepthComponent", "hasStencilComponent", "isCompressed", "packed", "planeCompatibleFormat", "planeCount", "planeHeightDivisor", "planeWidthDivisor", "texelsPerBlock" }; for ( auto const & func : formatTraitFunctions ) { formatTraitsUsings += replaceWithMap( usingTemplate, { { "function", func } } ); } return formatTraitsUsings; } std::string VulkanHppGenerator::generateCppModuleExtensionInspectionUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${function}; )" }; auto extensionInspectionsUsings = std::string{ R"( //====================================== //=== Extension inspection functions === //====================================== )" }; auto const extensionInspectionFunctions = std::array{ "getDeviceExtensions", "getInstanceExtensions", "getDeprecatedExtensions", "getExtensionDepends", "getObsoletedExtensions", "getPromotedExtensions", "getExtensionDeprecatedBy", "getExtensionObsoletedBy", "getExtensionPromotedTo", "isDeprecatedExtension", "isDeviceExtension", "isInstanceExtension", "isObsoletedExtension", "isPromotedExtension" }; for ( auto const & func : extensionInspectionFunctions ) { extensionInspectionsUsings += replaceWithMap( usingTemplate, { { "function", func } } ); } return extensionInspectionsUsings; } std::string VulkanHppGenerator::generateCppModuleUsings() const { auto const usingTemplate = std::string{ R"( using VULKAN_HPP_NAMESPACE::${className}; )" }; auto const hardCodedEnhancedModeTypes = std::array{ "ArrayProxy", "ArrayProxyNoTemporaries", "StridedArrayProxy", "Optional", "StructureChain" }; auto const hardCodedSmartHandleHelperTypes = std::array{ "ObjectDestroy", "ObjectFree", "ObjectRelease", "PoolFree", "ObjectDestroyShared", "ObjectFreeShared", "ObjectReleaseShared", "PoolFreeShared" }; auto const hardCodedFunctions = std::array{ "exchange" }; auto usings = std::string{ R"( //===================================== //=== HARDCODED TYPEs AND FUNCTIONs === //===================================== using VULKAN_HPP_NAMESPACE::ArrayWrapper1D; using VULKAN_HPP_NAMESPACE::ArrayWrapper2D; using VULKAN_HPP_NAMESPACE::Flags; using VULKAN_HPP_NAMESPACE::FlagTraits; namespace detail { using VULKAN_HPP_NAMESPACE::detail::DispatchLoaderBase; using VULKAN_HPP_NAMESPACE::detail::DispatchLoaderDynamic; #if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 using VULKAN_HPP_NAMESPACE::detail::defaultDispatchLoaderDynamic; #endif #if !defined( VK_NO_PROTOTYPES ) using VULKAN_HPP_NAMESPACE::detail::DispatchLoaderStatic; using VULKAN_HPP_NAMESPACE::detail::getDispatchLoaderStatic; #endif /*VK_NO_PROTOTYPES*/ using VULKAN_HPP_NAMESPACE::detail::createResultValueType; using VULKAN_HPP_NAMESPACE::detail::resultCheck; } #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) namespace VULKAN_HPP_RAII_NAMESPACE { using VULKAN_HPP_RAII_NAMESPACE::operator==; using VULKAN_HPP_RAII_NAMESPACE::operator!=; #if defined( VULKAN_HPP_HAS_SPACESHIP_OPERATOR ) using VULKAN_HPP_RAII_NAMESPACE::operator<=>; #else using VULKAN_HPP_RAII_NAMESPACE::operator<; #endif } #endif )" }; // insert the operators auto const operatorUsings = std::array{ "operator&", "operator|", "operator^", "operator~", "operator<", "operator<=", "operator>", "operator>=", "operator==", "operator!=" }; for ( auto const & operatorName : operatorUsings ) { usings += replaceWithMap( usingTemplate, { { "className", operatorName } } ); } // delete the namespace declaration for the default dispatcher macro using statement usings += R"(using VULKAN_HPP_DEFAULT_DISPATCHER_TYPE; )"; auto enhancedModeUsings = std::string{}; for ( auto const & className : hardCodedEnhancedModeTypes ) { enhancedModeUsings += replaceWithMap( usingTemplate, { { "className", std::string{ className } } } ); } // protect the enhanced-mode usings with a macro const auto [enterEnhancedMode, leaveEnhancedMode] = generateProtection( "VULKAN_HPP_DISABLE_ENHANCED_MODE", false ); usings += "\n" + enterEnhancedMode + enhancedModeUsings + leaveEnhancedMode + "\n"; std::string smartHandleUsings = " namespace detail\n {\n"; for ( auto const & helperType : hardCodedSmartHandleHelperTypes ) { smartHandleUsings += " using VULKAN_HPP_NAMESPACE::detail::"s + helperType + ";\n"; } smartHandleUsings += R"( } using VULKAN_HPP_NAMESPACE::SharedHandle; using VULKAN_HPP_NAMESPACE::UniqueHandle; )"; // likewise for the smart-handle usings const auto [enterNoSmartHandle, leaveNoSmartHandle] = generateProtection( "VULKAN_HPP_NO_SMART_HANDLE", false ); usings += "\n" + enterNoSmartHandle + smartHandleUsings + leaveNoSmartHandle + "\n"; for ( auto const & functionName : hardCodedFunctions ) { usings += "\n" + replaceWithMap( usingTemplate, { { "className", std::string{ functionName } } } ); } // now generate baseTypes auto baseTypes = std::string{ R"( //================== //=== BASE TYPEs === //================== )" }; for ( auto const & baseType : m_baseTypes ) { if ( baseType.first != "VkFlags" && baseType.first != "VkFlags64" && !baseType.second.typeInfo.type.empty() ) { baseTypes += replaceWithMap( usingTemplate, { { "className", stripPrefix( baseType.first, "Vk" ) } } ); } } usings += baseTypes; // generate Enums usings += generateCppModuleEnumUsings(); // to_string, toHexString auto const toString = std::array{ "to_string", "toHexString" }; auto const [toStringEnter, toStringLeave] = generateProtection( "VULKAN_HPP_NO_TO_STRING", false ); usings += R"( //====================== //=== ENUM to_string === //====================== )" + toStringEnter; for ( auto const & name : toString ) { usings += replaceWithMap( usingTemplate, { { "className", name } } ); } usings += toStringLeave + "\n"; // hardcoded exceptions and functions auto const hardCodedExceptionTypesAndFunctions = std::array{ "ErrorCategoryImpl", "Error", "LogicError", "SystemError", "errorCategory", "make_error_code", "make_error_condition" }; const auto [exceptionsEnter, exceptionsLeave] = generateProtection( "VULKAN_HPP_NO_EXCEPTIONS", false ); auto exceptionsUsings = std::string{ R"( //============================= //=== EXCEPTIONs AND ERRORs === //============================= )" } + exceptionsEnter; for ( auto const & name : hardCodedExceptionTypesAndFunctions ) { exceptionsUsings += replaceWithMap( usingTemplate, { { "className", name } } ); } usings += exceptionsUsings; // result Exceptions auto resultExceptionsUsings = std::string{}; auto const & [name, data] = *m_enums.find( "VkResult" ); for ( auto const & value : data.values ) { if ( value.supported && value.name.starts_with( "VK_ERROR" ) ) { auto [enter, leave] = generateProtection( value.protect ); enter = enter.empty() ? enter : "\n" + enter; leave = leave.empty() ? leave : leave + "\n"; auto const valueName = generateEnumValueName( name, value.name, false ); auto const className = stripPrefix( valueName, "eError" ) + "Error"; resultExceptionsUsings += enter + replaceWithMap( usingTemplate, { { "className", className } } ) + leave; } } usings += resultExceptionsUsings; usings += exceptionsLeave + "\n"; // some hardcoded types auto const hardCodedResultValueTypes = std::array{ "ResultValue", "ResultValueType" }; for ( auto const & valueType : hardCodedResultValueTypes ) { usings += replaceWithMap( usingTemplate, { { "className", valueType } } ); } usings += "\n" + generateConstexprUsings() + "\n"; // structs, handles, UniqueHandles, etc usings += generateCppModuleFuncpointerUsings(); usings += generateCppModuleStructUsings(); usings += generateCppModuleHandleUsings(); usings += generateCppModuleUniqueHandleUsings(); usings += generateCppModuleSharedHandleUsings(); usings += generateCppModuleFuncsUsings(); auto const [enterDisableEnhanced, leaveDisableEnhanced] = generateProtection( "VULKAN_HPP_DISABLE_ENHANCED_MODE", false ); usings += "\n" + enterDisableEnhanced + replaceWithMap( usingTemplate, { { "className", "StructExtends" } } ) + leaveDisableEnhanced + "\n"; auto const dynamicLoaderUsing = std::string{ R"(#if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL namespace detail { using VULKAN_HPP_NAMESPACE::detail::DynamicLoader; } #endif /*VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL*/ )" }; usings += dynamicLoaderUsing; usings += generateCppModuleFormatTraitsUsings(); usings += generateCppModuleExtensionInspectionUsings(); return usings; } std::string VulkanHppGenerator::generateCppModuleCommands() const { // generate PFN_* functions auto pfnCommands = std::string{ R"( //================== //=== PFN TYPEs === //================== )" }; std::set listedCommands; // some commands are listed with more than one extension! for ( auto const & feature : m_features ) { appendCppModuleCommands( feature.requireData, listedCommands, feature.name, pfnCommands ); } for ( auto const & extension : m_extensions ) { appendCppModuleCommands( extension.requireData, listedCommands, extension.name, pfnCommands ); } return pfnCommands; } std::string VulkanHppGenerator::generateCppModuleRaiiUsings() const { auto const raiiUsingTemplate = std::string{ R"( using VULKAN_HPP_RAII_NAMESPACE::${className}; )" }; auto usings = std::string{ R"( //====================== //=== RAII HARDCODED === //====================== using VULKAN_HPP_RAII_NAMESPACE::Context; using VULKAN_HPP_RAII_NAMESPACE::isVulkanRAIIHandleType; namespace detail { using VULKAN_HPP_RAII_NAMESPACE::detail::ContextDispatcher; using VULKAN_HPP_RAII_NAMESPACE::detail::InstanceDispatcher; using VULKAN_HPP_RAII_NAMESPACE::detail::DeviceDispatcher; } //==================== //=== RAII HANDLEs === //==================== )" }; auto const generateUsingsAndProtection = [&raiiUsingTemplate, this]( std::vector const & requireData, std::string const & title ) { std::string usings; forEachRequiredHandle( requireData, [&usings, &raiiUsingTemplate, this]( std::pair const & handleData ) { usings += replaceWithMap( raiiUsingTemplate, { { "className", stripPrefix( handleData.first, "Vk" ) } } ); // if there is an array constructor, generate the plural type also if ( hasArrayConstructor( handleData.second ) ) { usings += replaceWithMap( raiiUsingTemplate, { { "className", stripPrefix( handleData.first, "Vk" ) + "s" } } ); } } ); return addTitleAndProtection( title, usings ); }; // now, insert features and extensions with protection, and strip Vk prefix for ( auto const & feature : m_features ) { usings += generateUsingsAndProtection( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { usings += generateUsingsAndProtection( extension.requireData, extension.name ); } return usings; } std::string VulkanHppGenerator::generateCppModuleHandleHashSpecializations() const { const std::string hashesTemplate = R"( //======================================== //=== HASH specializations for handles === //======================================== ${specializations} )"; auto const generateSpecializations = [this]( std::vector const & requireData, std::string const & title ) { std::string specializations; forEachRequiredHandle( requireData, [&specializations]( std::pair const & handleData ) { specializations += " template <> struct hash;\n"; } ); return addTitleAndProtection( title, specializations ); }; std::string specializations; for ( auto const & feature : m_features ) { specializations += generateSpecializations( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { specializations += generateSpecializations( extension.requireData, extension.name ); } return replaceWithMap( hashesTemplate, { { "specializations", specializations } } ); } std::string VulkanHppGenerator::generateCppModuleHashSpecializations() const { std::string const hasSpecializationsTemplate = R"( //======================================= //=== HASH specialization for Flags types === //======================================= template struct hash>; ${handleHashSpecializations} ${structHashSpecializations} )"; return replaceWithMap( hasSpecializationsTemplate, { { "handleHashSpecializations", generateCppModuleHandleHashSpecializations() }, { "structHashSpecializations", generateCppModuleStructHashSpecializations() } } ); } std::string VulkanHppGenerator::generateCppModuleStructHashSpecializations() const { const std::string hashesTemplate = R"( //======================================== //=== HASH specializations for structs === //======================================== ${specializations} )"; auto const generateSpecializations = [this]( std::vector const & requireData, std::string const & title ) { std::string specializations; forEachRequiredStruct( requireData, [&specializations]( std::pair const & structData ) { specializations += " template <> struct hash;\n"; } ); return addTitleAndProtection( title, specializations ); }; std::string specializations; for ( auto const & feature : m_features ) { specializations += generateSpecializations( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { specializations += generateSpecializations( extension.requireData, extension.name ); } return replaceWithMap( hashesTemplate, { { "specializations", specializations } } ); } std::string VulkanHppGenerator::generateDataDeclarations( CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, std::set const & templatedParams, CommandFlavourFlags flavourFlags, bool raii, std::vector const & dataTypes, std::string const & dataType, std::string const & returnType, std::string const & returnVariable ) const { assert( dataTypes.size() == returnParams.size() ); switch ( returnParams.size() ) { case 0: return ""; // no returnParams -> no data declarations case 1: return generateDataDeclarations1Return( commandData, returnParams, vectorParams, templatedParams, flavourFlags, dataTypes, dataType, returnType, returnVariable ); case 2: assert( !( flavourFlags & CommandFlavourFlagBits::unique ) ); return generateDataDeclarations2Returns( commandData, returnParams, vectorParams, flavourFlags, raii, dataTypes, dataType, returnVariable ); case 3: assert( !( flavourFlags & ( CommandFlavourFlagBits::singular | CommandFlavourFlagBits::unique ) ) ); return generateDataDeclarations3Returns( commandData, returnParams, vectorParams, flavourFlags, raii, dataTypes ); default: assert( false ); return ""; } } std::string VulkanHppGenerator::generateDataDeclarations1Return( CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, std::set const & templatedParams, CommandFlavourFlags flavourFlags, std::vector const & dataTypes, std::string const & dataType, std::string const & returnType, std::string const & returnVariable ) const { auto vectorParamIt = vectorParams.find( returnParams[0] ); if ( !( flavourFlags & CommandFlavourFlagBits::chained ) ) { if ( ( vectorParamIt == vectorParams.end() ) || ( flavourFlags & CommandFlavourFlagBits::singular ) ) { std::string const dataDeclarationsTemplate = R"(${dataType} ${dataVariable};)"; if ( dataType.starts_with( "VULKAN_HPP_NAMESPACE::" ) || m_types.contains( "Vk" + dataType ) ) { return replaceWithMap( dataDeclarationsTemplate, { { "dataType", dataType }, { "dataVariable", returnVariable } } ); } else { return replaceWithMap( dataDeclarationsTemplate, { { "dataType", returnType }, { "dataVariable", returnVariable } } ); } } else { std::string allocator = stripPrefix( dataTypes[0], "VULKAN_HPP_NAMESPACE::" ) + "Allocator"; if ( vectorParamIt->second.byStructure ) { std::string vectorAllocator = ( ( flavourFlags & CommandFlavourFlagBits::withAllocator ) && !( flavourFlags & CommandFlavourFlagBits::unique ) ) ? ( "( " + startLowerCase( allocator ) + " )" ) : ""; std::string const dataDeclarationTemplate = R"(${dataType} ${returnVariable}${vectorAllocator}; ${structType} ${structVariable};)"; return replaceWithMap( dataDeclarationTemplate, { { "dataType", dataType }, { "returnVariable", returnVariable }, { "structType", stripPostfix( commandData.params[vectorParamIt->first].type.compose( "Vk" ), "*" ) }, { "structVariable", startLowerCase( stripPrefix( commandData.params[vectorParamIt->first].name, "p" ) ) }, { "vectorAllocator", stripPrefix( vectorAllocator, "," ) } } ); } else { std::string vectorAllocator = ( ( flavourFlags & CommandFlavourFlagBits::withAllocator ) && !( flavourFlags & CommandFlavourFlagBits::unique ) ) ? ( ", {}, " + startLowerCase( allocator ) ) : ""; std::string vectorSize = getVectorSize( commandData.params, vectorParams, returnParams[0], dataTypes[0], templatedParams ); std::string const dataDeclarationsTemplate = R"(${dataType} ${returnVariable}( ${vectorSize}${vectorAllocator} );)"; return replaceWithMap( dataDeclarationsTemplate, { { "dataType", dataType }, { "returnVariable", returnVariable }, { "vectorAllocator", vectorAllocator }, { "vectorSize", vectorSize } } ); } } } else { assert( ( vectorParamIt == vectorParams.end() ) || ( flavourFlags & CommandFlavourFlagBits::singular ) ); std::string dataVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); std::string const dataDeclarationsTemplate = R"(${returnType} ${returnVariable}; ${dataType} & ${dataVariable} = ${returnVariable}.template get<${dataType}>();)"; return replaceWithMap( dataDeclarationsTemplate, { { "dataType", dataTypes[0] }, { "dataVariable", dataVariable }, { "returnType", ( commandData.returnType.type == "void" ) ? returnType : "StructureChain" }, { "returnVariable", returnVariable } } ); } } std::string VulkanHppGenerator::generateDataDeclarations2Returns( CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, CommandFlavourFlags flavourFlags, bool raii, std::vector const & dataTypes, std::string const & returnType, std::string const & returnVariable ) const { const bool chained = flavourFlags & CommandFlavourFlagBits::chained; const bool singular = flavourFlags & CommandFlavourFlagBits::singular; const bool withAllocator = flavourFlags & CommandFlavourFlagBits::withAllocator; switch ( vectorParams.size() ) { case 0: assert( !singular && !chained ); { std::string firstDataVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); std::string secondDataVariable = startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ); std::string const dataDeclarationTemplate = R"(std::pair<${firstDataType},${secondDataType}> data_; ${firstDataType} & ${firstDataVariable} = data_.first; ${secondDataType} & ${secondDataVariable} = data_.second;)"; return replaceWithMap( dataDeclarationTemplate, { { "firstDataType", dataTypes[0] }, { "firstDataVariable", firstDataVariable }, { "secondDataType", dataTypes[1] }, { "secondDataVariable", secondDataVariable } } ); } case 1: assert( ( returnParams[0] == vectorParams.begin()->second.lenParam ) && ( returnParams[1] == vectorParams.begin()->first ) && !singular ); { std::string counterVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); if ( !chained ) { std::string vectorAllocator = withAllocator ? ( "( " + startLowerCase( stripPrefix( dataTypes[1], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator )" ) : ""; std::string const dataDeclarationTemplate = R"(${returnType} ${returnVariable}${vectorAllocator}; ${counterType} ${counterVariable};)"; return replaceWithMap( dataDeclarationTemplate, { { "counterType", dataTypes[0] }, { "counterVariable", counterVariable }, { "returnType", returnType }, { "returnVariable", returnVariable }, { "vectorAllocator", vectorAllocator } } ); } else { std::string structureChainAllocator = raii ? "" : ", StructureChainAllocator"; std::string structureChainInitializer = withAllocator ? ( "( structureChainAllocator )" ) : ""; std::string vectorVariable = startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ); std::string const dataDeclarationTemplate = R"(std::vector structureChains${structureChainInitializer}; std::vector<${vectorElementType}> ${vectorVariable}; ${counterType} ${counterVariable};)"; return replaceWithMap( dataDeclarationTemplate, { { "counterType", dataTypes[0] }, { "counterVariable", counterVariable }, { "structureChainAllocator", structureChainAllocator }, { "structureChainInitializer", structureChainInitializer }, { "vectorElementType", dataTypes[1] }, { "vectorVariable", vectorVariable }, } ); } } break; case 2: assert( ( returnParams[0] == std::next( vectorParams.begin() )->first ) && !vectorParams.contains( returnParams[1] ) && !chained ); { std::string firstDataVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); std::string secondDataVariable = startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ); if ( singular ) { firstDataVariable = stripPluralS( firstDataVariable ); std::string const dataDeclarationTemplate = R"(std::pair<${firstDataType},${secondDataType}> data_; ${firstDataType} & ${firstDataVariable} = data_.first; ${secondDataType} & ${secondDataVariable} = data_.second;)"; return replaceWithMap( dataDeclarationTemplate, { { "firstDataType", dataTypes[0] }, { "firstDataVariable", firstDataVariable }, { "secondDataType", dataTypes[1] }, { "secondDataVariable", secondDataVariable } } ); } else { std::string allocatorType = raii ? "" : ( startUpperCase( stripPrefix( dataTypes[0], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator" ); std::string allocateInitializer = withAllocator ? ( ", " + startLowerCase( allocatorType ) ) : ""; if ( !raii ) { allocatorType = ", " + allocatorType; } std::string vectorSize = startLowerCase( stripPrefix( commandData.params[vectorParams.begin()->first].name, "p" ) ) + ".size()"; std::string const dataDeclarationTemplate = R"(std::pair,${secondDataType}> data_( std::piecewise_construct, std::forward_as_tuple( ${vectorSize}${allocateInitializer} ), std::forward_as_tuple( 0 ) ); std::vector<${firstDataType}${allocatorType}> & ${firstDataVariable} = data_.first; ${secondDataType} & ${secondDataVariable} = data_.second;)"; return replaceWithMap( dataDeclarationTemplate, { { "allocateInitializer", allocateInitializer }, { "allocatorType", allocatorType }, { "firstDataType", dataTypes[0] }, { "firstDataVariable", firstDataVariable }, { "secondDataType", dataTypes[1] }, { "secondDataVariable", secondDataVariable }, { "vectorSize", vectorSize } } ); } } break; default: assert( false ); return ""; } } std::string VulkanHppGenerator::generateDataDeclarations3Returns( CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, CommandFlavourFlags flavourFlags, bool raii, std::vector const & dataTypes ) const { switch ( vectorParams.size() ) { case 0: { std::string const dataDeclarationsTemplate = R"( std::tuple<${firstType}, ${secondType}, ${thirdType}> data_; ${firstType} &${firstVariable} = std::get<0>(data_); ${secondType} &${secondVariable} = std::get<1>(data_); ${thirdType} &${thirdVariable} = std::get<2>(data_);)"; return replaceWithMap( dataDeclarationsTemplate, { { "firstType", dataTypes[0] }, { "firstVariable", startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ) }, { "secondType", dataTypes[1] }, { "secondVariable", startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ) }, { "thirdType", dataTypes[2] }, { "thirdVariable", startLowerCase( stripPrefix( commandData.params[returnParams[2]].name, "p" ) ) } } ); } break; case 1: { assert( ( returnParams[1] == vectorParams.begin()->second.lenParam ) && ( returnParams[2] == vectorParams.begin()->first ) ); std::string valueVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); std::string counterVariable = startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ); std::string vectorVariable = startLowerCase( stripPrefix( commandData.params[returnParams[2]].name, "p" ) ); std::string vectorAllocatorType, pairConstructor; if ( !raii ) { vectorAllocatorType = startUpperCase( stripPrefix( dataTypes[2], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator"; pairConstructor = ( flavourFlags & CommandFlavourFlagBits::withAllocator ) ? ( "( std::piecewise_construct, std::forward_as_tuple(), std::forward_as_tuple( " + startLowerCase( vectorAllocatorType ) + " ) )" ) : ""; vectorAllocatorType = ", " + vectorAllocatorType; } std::string const chainedDataDeclarationsTemplate = R"(std::pair,std::vector<${vectorElementType}${vectorAllocatorType}>> data_${pairConstructor}; ${valueType} & ${valueVariable} = data_.first.template get<${valueType}>(); std::vector<${vectorElementType}${vectorAllocatorType}> & ${vectorVariable} = data_.second; ${counterType} ${counterVariable};)"; std::string const dataDeclarationsTemplate = R"(std::pair<${valueType},std::vector<${vectorElementType}${vectorAllocatorType}>> data_${pairConstructor}; ${valueType} & ${valueVariable} = data_.first; std::vector<${vectorElementType}${vectorAllocatorType}> & ${vectorVariable} = data_.second; ${counterType} ${counterVariable};)"; return replaceWithMap( ( flavourFlags & CommandFlavourFlagBits::chained ) ? chainedDataDeclarationsTemplate : dataDeclarationsTemplate, { { "counterType", dataTypes[1] }, { "counterVariable", counterVariable }, { "vectorAllocatorType", vectorAllocatorType }, { "vectorElementType", dataTypes[2] }, { "vectorVariable", vectorVariable }, { "pairConstructor", pairConstructor }, { "valueType", dataTypes[0] }, { "valueVariable", valueVariable } } ); } break; case 2: { assert( !( flavourFlags & CommandFlavourFlagBits::chained ) ); assert( ( returnParams[0] == vectorParams.begin()->second.lenParam ) && ( returnParams[1] == vectorParams.begin()->first ) && ( returnParams[2] == std::next( vectorParams.begin() )->first ) && ( returnParams[0] == std::next( vectorParams.begin() )->second.lenParam ) ); std::string counterVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); std::string firstVectorVariable = startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ); std::string secondVectorVariable = startLowerCase( stripPrefix( commandData.params[returnParams[2]].name, "p" ) ); std::string firstVectorAllocatorType, secondVectorAllocatorType, pairConstructor; if ( !raii ) { firstVectorAllocatorType = startUpperCase( stripPrefix( dataTypes[1], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator"; secondVectorAllocatorType = startUpperCase( stripPrefix( dataTypes[2], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator"; pairConstructor = ( flavourFlags & CommandFlavourFlagBits::withAllocator ) ? ( "( std::piecewise_construct, std::forward_as_tuple( " + startLowerCase( firstVectorAllocatorType ) + " ), std::forward_as_tuple( " + startLowerCase( secondVectorAllocatorType ) + " ) )" ) : ""; firstVectorAllocatorType = ", " + firstVectorAllocatorType; secondVectorAllocatorType = ", " + secondVectorAllocatorType; } std::string const dataDeclarationsTemplate = R"(std::pair, std::vector<${secondVectorElementType}${secondVectorAllocatorType}>> data_${pairConstructor}; std::vector<${firstVectorElementType}${firstVectorAllocatorType}> & ${firstVectorVariable} = data_.first; std::vector<${secondVectorElementType}${secondVectorAllocatorType}> & ${secondVectorVariable} = data_.second; ${counterType} ${counterVariable};)"; return replaceWithMap( dataDeclarationsTemplate, { { "counterType", dataTypes[0] }, { "counterVariable", counterVariable }, { "firstVectorAllocatorType", firstVectorAllocatorType }, { "firstVectorElementType", dataTypes[1] }, { "firstVectorVariable", firstVectorVariable }, { "pairConstructor", pairConstructor }, { "secondVectorAllocatorType", secondVectorAllocatorType }, { "secondVectorElementType", dataTypes[2] }, { "secondVectorVariable", secondVectorVariable } } ); } default: assert( false ); return ""; } } std::string VulkanHppGenerator::generateDataPreparation( CommandData const & commandData, size_t initialSkipCount, std::vector const & returnParams, std::map const & vectorParams, std::set const & templatedParams, CommandFlavourFlags flavourFlags, bool enumerating, std::vector const & dataTypes ) const { const bool chained = flavourFlags & CommandFlavourFlagBits::chained; const bool singular = flavourFlags & CommandFlavourFlagBits::singular; const bool unique = flavourFlags & CommandFlavourFlagBits::unique; auto vectorParamIt = ( 1 < returnParams.size() ) ? vectorParams.find( returnParams[1] ) : vectorParams.end(); if ( vectorParamIt != vectorParams.end() ) { assert( !unique ); std::string vectorName = startLowerCase( stripPrefix( commandData.params[vectorParamIt->first].name, "p" ) ); if ( chained ) { assert( !singular ); assert( templatedParams.empty() ); assert( returnParams.size() == 2 ); assert( !vectorParams.contains( returnParams[0] ) ); assert( ( vectorParamIt != vectorParams.end() ) && ( vectorParamIt->second.lenParam == returnParams[0] ) ); std::string vectorElementType = stripPostfix( commandData.params[vectorParamIt->first].type.compose( "Vk" ), " *" ); if ( enumerating ) { std::string const dataPreparationTemplate = R"(VULKAN_HPP_ASSERT( ${counterName} <= ${vectorName}.size() ); if ( ${counterName} < ${vectorName}.size() ) { structureChains.resize( ${counterName} ); } for ( ${counterType} i = 0; i < ${counterName}; i++ ) { structureChains[i].template get<${vectorElementType}>() = ${vectorName}[i]; })"; return replaceWithMap( dataPreparationTemplate, { { "counterName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ) }, { "counterType", commandData.params[vectorParamIt->second.lenParam].type.type }, { "vectorElementType", vectorElementType }, { "vectorName", vectorName } } ); } else { std::string const dataPreparationTemplate = R"(for ( ${counterType} i = 0; i < ${counterName}; i++ ) { structureChains[i].template get<${vectorElementType}>() = ${vectorName}[i]; })"; return replaceWithMap( dataPreparationTemplate, { { "counterName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ) }, { "counterType", commandData.params[vectorParamIt->second.lenParam].type.type }, { "vectorElementType", vectorElementType }, { "vectorName", vectorName } } ); } } else if ( enumerating ) { assert( !singular ); assert( ( vectorParams.size() != 2 ) || ( ( vectorParams.begin()->first == returnParams[1] ) && ( vectorParams.begin()->second.lenParam == returnParams[0] ) && ( std::next( vectorParams.begin() )->first == returnParams[2] ) && ( std::next( vectorParams.begin() )->second.lenParam == returnParams[0] ) ) ); std::string resizes; for ( auto const & vp : vectorParams ) { assert( std::ranges::any_of( returnParams, [&vp]( size_t rp ) { return rp == vp.first; } ) && std::ranges::any_of( returnParams, [&vp]( size_t rp ) { return rp == vp.second.lenParam; } ) ); resizes += startLowerCase( stripPrefix( commandData.params[vp.first].name, "p" ) ) + ".resize( " + startLowerCase( stripPrefix( commandData.params[vp.second.lenParam].name, "p" ) ) + " );\n"; } resizes.pop_back(); std::string const dataPreparationTemplate = R"(VULKAN_HPP_ASSERT( ${counterName} <= ${vectorName}.size() ); if ( ${counterName} < ${vectorName}.size() ) { ${resizes} })"; return replaceWithMap( dataPreparationTemplate, { { "counterName", startLowerCase( stripPrefix( commandData.params[vectorParamIt->second.lenParam].name, "p" ) ) }, { "resizes", resizes }, { "vectorName", vectorName } } ); } } else if ( unique && !singular && ( returnParams.size() == 1 ) && vectorParams.contains( returnParams[0] ) ) { std::string className = initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : ""; std::string deleterDefinition, vectorName, vectorSize; vectorParamIt = vectorParams.find( returnParams[0] ); if ( vectorParamIt != vectorParams.end() && vectorParamIt->second.byStructure ) { deleterDefinition = "detail::ObjectDestroy<" + className + ", Dispatch> deleter( *this, allocator, d )"; auto structIt = m_structs.find( commandData.params[returnParams[0]].type.type ); assert( structIt != m_structs.end() ); vectorName = startLowerCase( stripPrefix( structIt->second.members.back().name, "p" ) ); vectorSize = vectorName + ".size()"; } else { std::vector lenParts = tokenize( commandData.params[returnParams[0]].lenExpression, "->" ); switch ( lenParts.size() ) { case 1: deleterDefinition = "detail::ObjectDestroy<" + className + ", Dispatch> deleter( *this, allocator, d )"; break; case 2: { auto vpiIt = vectorParams.find( returnParams[0] ); assert( vpiIt != vectorParams.end() ); std::string poolType, poolName; std::tie( poolType, poolName ) = getPoolTypeAndName( commandData.params[vpiIt->second.lenParam].type.type ); assert( !poolType.empty() ); poolType = stripPrefix( poolType, "Vk" ); poolName = startLowerCase( stripPrefix( lenParts[0], "p" ) ) + "." + poolName; deleterDefinition = "detail::PoolFree<" + className + ", " + poolType + ", Dispatch> deleter( *this, " + poolName + ", d )"; } break; default: assert( false ); break; } vectorName = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); vectorSize = getVectorSize( commandData.params, vectorParams, returnParams[0], commandData.params[returnParams[0]].type.type, templatedParams ); } std::string handleType = stripPrefix( dataTypes[0], "VULKAN_HPP_NAMESPACE::" ); std::string vectorAllocator = ( flavourFlags & CommandFlavourFlagBits::withAllocator ) ? ( "( " + startLowerCase( handleType ) + "Allocator )" ) : ""; std::string elementName = stripPluralS( vectorName ); std::string uniqueVectorName = "unique" + startUpperCase( vectorName ); std::string const dataPreparationTemplate = R"(std::vector, ${handleType}Allocator> ${uniqueVectorName}${vectorAllocator}; ${uniqueVectorName}.reserve( ${vectorSize} ); ${deleterDefinition}; for ( auto const & ${elementName} : ${vectorName} ) { ${uniqueVectorName}.push_back( UniqueHandle<${handleType}, Dispatch>( ${elementName}, deleter ) ); })"; return replaceWithMap( dataPreparationTemplate, { { "elementName", elementName }, { "deleterDefinition", deleterDefinition }, { "handleType", handleType }, { "uniqueVectorName", uniqueVectorName }, { "vectorAllocator", vectorAllocator }, { "vectorName", vectorName }, { "vectorSize", vectorSize } } ); } return ""; } std::string VulkanHppGenerator::generateDataSizeChecks( CommandData const & commandData, std::vector const & returnParams, std::vector const & returnParamTypes, std::map const & vectorParams, std::set const & templatedParams, bool singular ) const { assert( returnParams.size() == returnParamTypes.size() ); std::string dataSizeChecks; if ( !singular ) { const std::string dataSizeCheckTemplate = R"( VULKAN_HPP_ASSERT( ${dataSize} % sizeof( ${dataType} ) == 0 );)"; for ( size_t i = 0; i < returnParams.size(); i++ ) { auto vectorParamIt = vectorParams.find( returnParams[i] ); if ( ( vectorParamIt != vectorParams.end() ) && templatedParams.contains( returnParams[i] ) && std::ranges::none_of( returnParams, [&vectorParamIt]( size_t rp ) noexcept { return rp == vectorParamIt->second.lenParam; } ) ) { dataSizeChecks += replaceWithMap( dataSizeCheckTemplate, { { "dataSize", commandData.params[vectorParamIt->second.lenParam].name }, { "dataType", returnParamTypes[i] } } ); } } } return dataSizeChecks; } std::string VulkanHppGenerator::generateDebugReportObjectType( std::string const & objectType ) const { std::string debugReportObjectType = objectType; debugReportObjectType = debugReportObjectType.replace( 3, 0, "DEBUG_REPORT_" ) + "_EXT"; auto enumIt = m_enums.find( "VkDebugReportObjectTypeEXT" ); assert( enumIt != m_enums.end() ); return containsName( enumIt->second.values, debugReportObjectType ) ? generateEnumValueName( enumIt->first, debugReportObjectType, false ) : "eUnknown"; } std::string VulkanHppGenerator::generateDecoratedReturnType( CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, bool enumerating, CommandFlavourFlags flavourFlags, std::string const & returnType ) const { const bool chained = flavourFlags & CommandFlavourFlagBits::chained; #if !defined( NDEBUG ) const bool unique = flavourFlags & CommandFlavourFlagBits::unique; #endif assert( !( chained && unique ) ); std::string decoratedReturnType; if ( ( 1 < commandData.successCodes.size() ) && returnParams.empty() && !chained ) { assert( ( commandData.returnType.type == "VkResult" ) && !unique ); decoratedReturnType = "Result"; } else if ( ( commandData.returnType.type != "VkResult" ) && ( commandData.returnType.type != "void" ) && returnParams.empty() ) { assert( !chained && !unique ); if ( commandData.returnType.type.starts_with( "Vk" ) ) { decoratedReturnType = stripPrefix( commandData.returnType.type, "Vk" ); } else if ( commandData.returnType.type.starts_with( "PFN_vk" ) ) { decoratedReturnType = "PFN_" + stripPrefix( commandData.returnType.type, "PFN_vk" ); } else { decoratedReturnType = commandData.returnType.type; } } else if ( ( commandData.returnType.type == "void" ) || ( ( commandData.returnType.type == "VkResult" ) && ( commandData.successCodes.size() == 1 ) && commandData.errorCodes.empty() ) ) { assert( !unique ); assert( ( commandData.returnType.type != "void" ) || ( returnParams.size() <= 2 ) ); decoratedReturnType = returnType; } else if ( commandData.returnType.type == "VkResult" ) { assert( !commandData.successCodes.empty() && ( commandData.successCodes[0] == "VK_SUCCESS" ) ); if ( ( commandData.successCodes.size() == 1 ) || ( ( commandData.successCodes.size() == 2 ) && ( commandData.successCodes[1] == "VK_INCOMPLETE" ) && enumerating ) ) { decoratedReturnType = "typename ResultValueType<" + returnType + ">::type"; if ( std::ranges::find( commandData.errorCodes, "VK_ERROR_OUT_OF_DATE_KHR" ) != commandData.errorCodes.end() ) { static std::string decoratedReturnTypeTemplate = R"( #if defined( VULKAN_HPP_HANDLE_ERROR_OUT_OF_DATE_AS_SUCCESS ) ResultValue<${returnType}> #else ${decoratedReturnType} #endif )"; decoratedReturnType = replaceWithMap( decoratedReturnTypeTemplate, { { "decoratedReturnType", decoratedReturnType }, { "returnType", returnType } } ); } } else if ( !commandData.errorCodes.empty() && ( 1 < commandData.successCodes.size() ) && ( ( returnParams.size() == 1 ) || ( ( returnParams.size() == 2 ) && ( vectorParams.empty() || ( ( vectorParams.size() == 1 ) && ( commandData.successCodes.size() == 3 ) && ( commandData.successCodes[1] == "VK_INCOMPLETE" ) ) ) ) || ( ( returnParams.size() == 3 ) && vectorParams.empty() ) ) ) { decoratedReturnType = "ResultValue<" + returnType + ">"; } else { assert( false ); } } else { decoratedReturnType = "std::pair<" + commandData.returnType.compose( "Vk" ) + ", " + returnType + ">"; } return decoratedReturnType; } std::string VulkanHppGenerator::generateDeprecatedConstructors( std::string const & name ) const { std::string str; if ( name == "VkAllocationCallbacks" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This constructor is deprecated. Use the one taking function pointer types from the vk-namespace instead." ) AllocationCallbacks( void * pUserData_, PFN_vkAllocationFunction pfnAllocation_, PFN_vkReallocationFunction pfnReallocation_ = {}, PFN_vkFreeFunction pfnFree_ = {}, PFN_vkInternalAllocationNotification pfnInternalAllocation_ = {}, PFN_vkInternalFreeNotification pfnInternalFree_ = {} ) VULKAN_HPP_NOEXCEPT : AllocationCallbacks( pUserData_, reinterpret_cast( pfnAllocation_ ), reinterpret_cast( pfnReallocation_ ), reinterpret_cast( pfnFree_ ), reinterpret_cast( pfnInternalAllocation_ ), reinterpret_cast( pfnInternalFree_ ) ) { } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "VkDebugReportCallbackCreateInfoEXT" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This constructor is deprecated. Use the one taking function pointer types from the vk-namespace instead." ) DebugReportCallbackCreateInfoEXT( DebugReportFlagsEXT flags_, PFN_vkDebugReportCallbackEXT pfnCallback_, void * pUserData_ = {}, const void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT : DebugReportCallbackCreateInfoEXT( flags_, reinterpret_cast( pfnCallback_ ), pUserData_, pNext_ ) { } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "VkDebugUtilsMessengerCreateInfoEXT" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This constructor is deprecated. Use the one taking function pointer types from the vk-namespace instead." ) DebugUtilsMessengerCreateInfoEXT( DebugUtilsMessengerCreateFlagsEXT flags_, DebugUtilsMessageSeverityFlagsEXT messageSeverity_, DebugUtilsMessageTypeFlagsEXT messageType_, PFN_vkDebugUtilsMessengerCallbackEXT pfnUserCallback_, void * pUserData_ = {}, const void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT : DebugUtilsMessengerCreateInfoEXT( flags_, messageSeverity_, messageType_, reinterpret_cast( pfnUserCallback_ ), pUserData_, pNext_ ) { } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "VkDeviceDeviceMemoryReportCreateInfoEXT" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This constructor is deprecated. Use the one taking function pointer types from the vk-namespace instead." ) DeviceDeviceMemoryReportCreateInfoEXT( DeviceMemoryReportFlagsEXT flags_, PFN_vkDeviceMemoryReportCallbackEXT pfnUserCallback_, void * pUserData_ = {}, const void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT : DeviceDeviceMemoryReportCreateInfoEXT( flags_, reinterpret_cast( pfnUserCallback_ ), pUserData_, pNext_ ) { } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "DirectDriverLoadingInfoLUNARG" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This constructor is deprecated. Use the one taking function pointer types from the vk-namespace instead." ) DirectDriverLoadingInfoLUNARG( VULKAN_HPP_NAMESPACE::DirectDriverLoadingFlagsLUNARG flags_, PFN_vkGetInstanceProcAddrLUNARG pfnGetInstanceProcAddr_, void * pNext_ = nullptr ) VULKAN_HPP_NOEXCEPT : DirectDriverLoadingInfoLUNARG( flags_, reinterpret_cast( pfnGetInstanceProcAddr_ ), pNext_ ) { } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } return str; } std::string VulkanHppGenerator::generateDeprecatedStructSetters( std::string const & name ) const { std::string str; if ( name == "VkAllocationCallbacks" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) AllocationCallbacks & setPfnAllocation( PFN_vkAllocationFunction pfnAllocation_ ) VULKAN_HPP_NOEXCEPT { return setPfnAllocation( reinterpret_cast( pfnAllocation_ ) ); } VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) AllocationCallbacks & setPfnReallocation( PFN_vkReallocationFunction pfnReallocation_ ) VULKAN_HPP_NOEXCEPT { return setPfnReallocation( reinterpret_cast( pfnReallocation_ ) ); } VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) AllocationCallbacks & setPfnInternalAllocation( PFN_vkInternalAllocationNotification pfnInternalAllocation_ ) VULKAN_HPP_NOEXCEPT { return setPfnInternalAllocation( reinterpret_cast( pfnInternalAllocation_ ) ); } VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) AllocationCallbacks & setPfnInternalFree( PFN_vkInternalFreeNotification pfnInternalFree_ ) VULKAN_HPP_NOEXCEPT { return setPfnInternalFree( reinterpret_cast( pfnInternalFree_ ) ); } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "VkDebugReportCallbackCreateInfoEXT" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) DebugReportCallbackCreateInfoEXT & setPfnCallback( PFN_vkDebugReportCallbackEXT pfnCallback_ ) VULKAN_HPP_NOEXCEPT { return setPfnCallback( reinterpret_cast( pfnCallback_ ) ); } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "VkDebugUtilsMessengerCreateInfoEXT" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) DebugUtilsMessengerCreateInfoEXT & setPfnUserCallback( PFN_vkDebugUtilsMessengerCallbackEXT pfnUserCallback_ ) VULKAN_HPP_NOEXCEPT { return setPfnUserCallback( reinterpret_cast( pfnUserCallback_ ) ); } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "VkDeviceDeviceMemoryReportCreateInfoEXT" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) DeviceDeviceMemoryReportCreateInfoEXT & setPfnUserCallback( PFN_vkDeviceMemoryReportCallbackEXT pfnUserCallback_ ) VULKAN_HPP_NOEXCEPT { return setPfnUserCallback( reinterpret_cast( pfnUserCallback_ ) ); } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } else if ( name == "DirectDriverLoadingInfoLUNARG" ) { // To be removed around December 2025 str = R"( #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic push # if defined( __clang__ ) # pragma clang diagnostic ignored "-Wunknown-warning-option" # endif # pragma GCC diagnostic ignored "-Wcast-function-type" #endif VULKAN_HPP_DEPRECATED( "This setter is deprecated. Use the one taking a function pointer type from the vk-namespace instead." ) DirectDriverLoadingInfoLUNARG & setPfnGetInstanceProcAddr( PFN_vkGetInstanceProcAddrLUNARG pfnGetInstanceProcAddr_ ) VULKAN_HPP_NOEXCEPT { return setPfnGetInstanceProcAddr( reinterpret_cast( pfnGetInstanceProcAddr_ ) ); } #if defined( __clang__ ) || defined( __GNUC__ ) # pragma GCC diagnostic pop #endif )"; } return str; } std::string VulkanHppGenerator::generateDispatchLoaderDynamic() const { const std::string dispatchLoaderDynamicTemplate = R"( using PFN_dummy = void ( * )(); class DispatchLoaderDynamic : public DispatchLoaderBase { public: ${commandMembers} public: DispatchLoaderDynamic() VULKAN_HPP_NOEXCEPT = default; DispatchLoaderDynamic( DispatchLoaderDynamic const & rhs ) VULKAN_HPP_NOEXCEPT = default; DispatchLoaderDynamic(PFN_vkGetInstanceProcAddr getInstanceProcAddr) VULKAN_HPP_NOEXCEPT { init(getInstanceProcAddr); } // This interface does not require a linked vulkan library. DispatchLoaderDynamic( VkInstance instance, PFN_vkGetInstanceProcAddr getInstanceProcAddr, VkDevice device = {}, PFN_vkGetDeviceProcAddr getDeviceProcAddr = nullptr ) VULKAN_HPP_NOEXCEPT { init( instance, getInstanceProcAddr, device, getDeviceProcAddr ); } template void init() { static DynamicLoader dl; init( dl ); } template void init( DynamicLoader const & dl ) VULKAN_HPP_NOEXCEPT { PFN_vkGetInstanceProcAddr getInstanceProcAddr = dl.template getProcAddress( "vkGetInstanceProcAddr" ); init( getInstanceProcAddr ); } void init( PFN_vkGetInstanceProcAddr getInstanceProcAddr ) VULKAN_HPP_NOEXCEPT { VULKAN_HPP_ASSERT(getInstanceProcAddr); vkGetInstanceProcAddr = getInstanceProcAddr; ${initialCommandAssignments} } // This interface does not require a linked vulkan library. void init( VkInstance instance, PFN_vkGetInstanceProcAddr getInstanceProcAddr, VkDevice device = {}, PFN_vkGetDeviceProcAddr /*getDeviceProcAddr*/ = nullptr ) VULKAN_HPP_NOEXCEPT { VULKAN_HPP_ASSERT(instance && getInstanceProcAddr); vkGetInstanceProcAddr = getInstanceProcAddr; init( Instance(instance) ); if (device) { init( Device(device) ); } } void init( Instance instanceCpp ) VULKAN_HPP_NOEXCEPT { VkInstance instance = static_cast( instanceCpp ); ${instanceCommandAssignments} } void init( Device deviceCpp ) VULKAN_HPP_NOEXCEPT { VkDevice device = static_cast( deviceCpp ); ${deviceCommandAssignments} } template void init( Instance const & instance, Device const & device, DynamicLoader const & dl ) VULKAN_HPP_NOEXCEPT { PFN_vkGetInstanceProcAddr getInstanceProcAddr = dl.template getProcAddress( "vkGetInstanceProcAddr" ); PFN_vkGetDeviceProcAddr getDeviceProcAddr = dl.template getProcAddress( "vkGetDeviceProcAddr" ); init( static_cast( instance ), getInstanceProcAddr, static_cast( device ), device ? getDeviceProcAddr : nullptr ); } template void init( Instance const & instance, Device const & device ) VULKAN_HPP_NOEXCEPT { static DynamicLoader dl; init(instance, device, dl); } }; )"; std::string commandMembers, deviceCommandAssignments, initialCommandAssignments, instanceCommandAssignments; std::set listedCommands; // some commands are listed with more than one extension! for ( auto const & feature : m_features ) { commandMembers += generateDispatchLoaderDynamicCommandMembers( feature.requireData, listedCommands, feature.name ); initialCommandAssignments += generateDispatchLoaderDynamicInitialCommandAssignment( feature.requireData, listedCommands, feature.name ); instanceCommandAssignments += generateDispatchLoaderDynamicInstanceCommandAssignment( feature.requireData, listedCommands, feature.name ); deviceCommandAssignments += generateDispatchLoaderDynamicDeviceCommandAssignment( feature.requireData, listedCommands, feature.name ); forEachRequiredCommand( feature.requireData, [&listedCommands]( NameLine const & command, auto const & ) { listedCommands.insert( command.name ); } ); } for ( auto const & extension : m_extensions ) { commandMembers += generateDispatchLoaderDynamicCommandMembers( extension.requireData, listedCommands, extension.name ); initialCommandAssignments += generateDispatchLoaderDynamicInitialCommandAssignment( extension.requireData, listedCommands, extension.name ); instanceCommandAssignments += generateDispatchLoaderDynamicInstanceCommandAssignment( extension.requireData, listedCommands, extension.name ); deviceCommandAssignments += generateDispatchLoaderDynamicDeviceCommandAssignment( extension.requireData, listedCommands, extension.name ); forEachRequiredCommand( extension.requireData, [&listedCommands]( NameLine const & command, auto const & ) { listedCommands.insert( command.name ); } ); } return replaceWithMap( dispatchLoaderDynamicTemplate, { { "commandMembers", commandMembers }, { "deviceCommandAssignments", deviceCommandAssignments }, { "initialCommandAssignments", initialCommandAssignments }, { "instanceCommandAssignments", instanceCommandAssignments } } ); } std::string VulkanHppGenerator::generateDispatchLoaderDynamicCommandMembers( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string members, placeholders; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & ) { if ( !listedCommands.contains( command.name ) ) { members += " PFN_" + command.name + " " + command.name + " = 0;\n"; placeholders += " PFN_dummy " + command.name + "_placeholder = 0;\n"; } } ); return addTitleAndProtection( title, members, placeholders ); } std::string VulkanHppGenerator::generateDispatchLoaderDynamicDeviceCommandAssignment( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string deviceCommandAssignments; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && !commandData.second.handle.empty() && isDeviceCommand( commandData.second ) ) { deviceCommandAssignments += generateDispatchLoaderDynamicCommandAssignment( command.name, commandData.first, "device" ); } } ); return addTitleAndProtection( title, deviceCommandAssignments ); } std::string VulkanHppGenerator::generateDispatchLoaderDynamicInitialCommandAssignment( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string initialCommandAssignments; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && commandData.second.handle.empty() ) { initialCommandAssignments += generateDispatchLoaderDynamicCommandAssignment( command.name, commandData.first, "NULL" ); } } ); return addTitleAndProtection( title, initialCommandAssignments ); } std::string VulkanHppGenerator::generateDispatchLoaderDynamicInstanceCommandAssignment( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string instanceCommandAssignments; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && !commandData.second.handle.empty() ) { instanceCommandAssignments += generateDispatchLoaderDynamicCommandAssignment( command.name, commandData.first, "instance" ); } } ); return addTitleAndProtection( title, instanceCommandAssignments ); } std::string VulkanHppGenerator::generateDispatchLoaderStatic() const { const std::string dispatchLoaderStaticTemplate = R"( #if !defined( VK_NO_PROTOTYPES ) || ( defined( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC ) && ( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 0 ) ) class DispatchLoaderStatic : public DispatchLoaderBase { public: ${commands} }; inline DispatchLoaderStatic & getDispatchLoaderStatic() { static DispatchLoaderStatic dls; return dls; } #endif )"; std::string commands; std::set listedCommands; for ( auto const & feature : m_features ) { commands += generateDispatchLoaderStaticCommands( feature.requireData, listedCommands, feature.name ); } for ( auto const & extension : m_extensions ) { commands += generateDispatchLoaderStaticCommands( extension.requireData, listedCommands, extension.name ); } return replaceWithMap( dispatchLoaderStaticTemplate, { { "commands", commands } } ); } std::string VulkanHppGenerator::generateDestroyCommand( std::string const & name, CommandData const & commandData ) const { // special handling for destroy functions, filter out alias functions std::string commandName = generateCommandName( name, commandData.params, 1 ); if ( m_commands.contains( name ) && ( ( ( name.substr( 2, 7 ) == "Destroy" ) && ( commandName != "destroy" ) ) || ( name.substr( 2, 4 ) == "Free" ) || ( name == "vkReleasePerformanceConfigurationINTEL" ) ) ) { assert( 1 < commandData.params.size() ); // make sure, the object to destroy/free/release is not optional in the shortened version! CommandData localCommandData = commandData; localCommandData.params[1].optional = false; std::string destroyCommandString = generateCommand( name, localCommandData, 1, false, false ); std::string shortenedName; if ( name.substr( 2, 7 ) == "Destroy" ) { shortenedName = "destroy"; } else if ( name.substr( 2, 4 ) == "Free" ) { // enclose "free" in parenthesis to prevent interference with MSVC debug free shortenedName = "( free )"; } else { assert( name == "vkReleasePerformanceConfigurationINTEL" ); shortenedName = "release"; } size_t pos = destroyCommandString.find( commandName ); while ( pos != std::string::npos ) { destroyCommandString.replace( pos, commandName.length(), shortenedName ); pos = destroyCommandString.find( commandName, pos ); } // we need to remove the default argument for the first argument, to prevent ambiguities! assert( 1 < localCommandData.params.size() ); pos = destroyCommandString.find( localCommandData.params[1].name ); // skip the standard version of the function assert( pos != std::string::npos ); pos = destroyCommandString.find( localCommandData.params[1].name, pos + 1 ); // get the argument to destroy in the advanced version assert( pos != std::string::npos ); pos = destroyCommandString.find( " VULKAN_HPP_DEFAULT_ASSIGNMENT( {} )", pos ); if ( pos != std::string::npos ) { destroyCommandString.erase( pos, strlen( " VULKAN_HPP_DEFAULT_ASSIGNMENT( {} )" ) ); } return "\n" + destroyCommandString; } return ""; } std::string VulkanHppGenerator::generateDispatchLoaderDynamicCommandAssignment( std::string const & commandName, std::string const & aliasName, std::string const & firstArg ) const { if ( commandName == "vkGetInstanceProcAddr" ) { // Don't overwite vkGetInstanceProcAddr with NULL. return ""; } std::string str = " " + commandName + " = PFN_" + commandName + "( vkGet" + ( ( firstArg == "device" ) ? "Device" : "Instance" ) + "ProcAddr( " + firstArg + ", \"" + commandName + "\" ) );\n"; // if this is an alias'ed function, use it as a fallback for the original one if ( commandName != aliasName ) { str += " if ( !" + aliasName + " ) " + aliasName + " = " + commandName + ";\n"; } return str; } std::string VulkanHppGenerator::generateDispatchLoaderStaticCommands( std::vector const & requireData, std::set & listedCommands, std::string const & title ) const { std::string str; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { // some commands are listed for multiple extensions ! if ( listedCommands.insert( command.name ).second ) { str += "\n"; std::string parameterList, parameters; assert( !commandData.second.params.empty() ); for ( auto param : commandData.second.params ) { parameterList += param.type.compose( "" ) + " " + param.name + generateCArraySizes( param.arraySizes ) + ", "; parameters += param.name + ", "; } assert( parameterList.ends_with( ", " ) && parameters.ends_with( ", " ) ); parameterList.resize( parameterList.size() - 2 ); parameters.resize( parameters.size() - 2 ); const std::string commandTemplate = R"( ${returnType} ${commandName}( ${parameterList} ) const VULKAN_HPP_NOEXCEPT { return ::${commandName}( ${parameters} ); } )"; str += replaceWithMap( commandTemplate, { { "commandName", command.name }, { "parameterList", parameterList }, { "parameters", parameters }, { "returnType", commandData.second.returnType.type } } ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateEnum( std::pair const & enumData, std::string const & surroundingProtect ) const { std::string baseType, bitmask; if ( enumData.second.isBitmask ) { auto bitmaskIt = std::ranges::find_if( m_bitmasks, [&enumData]( auto const & bitmask ) { return bitmask.second.require == enumData.first; } ); assert( bitmaskIt != m_bitmasks.end() ); baseType = " : " + bitmaskIt->first; bitmask = generateBitmask( bitmaskIt, surroundingProtect ); } std::string enumValues, previousEnter, previousLeave; std::map valueToNameMap; for ( auto const & value : enumData.second.values ) { if ( value.supported ) { std::string valueName = generateEnumValueName( enumData.first, value.name, enumData.second.isBitmask ); checkForError( valueToNameMap.insert( { valueName, value.name } ).second, value.xmlLine, "generated enum value name <" + valueName + "> already listed for enum <" + enumData.first + ">" ); // if the value's protect differs from the surrounding protect, generate protection code std::string enter, leave; if ( !value.protect.empty() && ( value.protect != surroundingProtect ) ) { tie( enter, leave ) = generateProtection( value.protect ); } if ( previousEnter != enter ) { enumValues += previousLeave + enter; } enumValues += " " + valueName; if ( value.deprecated ) { enumValues += " VULKAN_HPP_DEPRECATED_17( \"" + valueName + " is deprecated, but no reason was given in the API XML\" )"; } enumValues += " = " + value.name + ",\n"; for ( auto const & valueAlias : value.aliases ) { // only generate supported aliases if ( valueAlias.supported ) { std::string enumName = enumData.first; for ( auto aliasIt = enumData.second.aliases.begin(); ( aliasIt != enumData.second.aliases.end() ) && ( enumName == enumData.first ); ++aliasIt ) { auto enumAliasIt = enumData.second.aliases.begin(); std::string enumTag = findTag( enumData.first ); std::string aliasTag = findTag( enumAliasIt->first ); std::string valueTag = findTag( valueAlias.name ); if ( ( stripPostfix( enumData.first, enumTag ) == stripPostfix( enumAliasIt->first, aliasTag ) ) && ( aliasTag == valueTag ) ) { enumName = enumAliasIt->first; } } std::string aliasName = generateEnumValueName( enumName, valueAlias.name, enumData.second.isBitmask ); std::map::const_iterator mapIt; bool inserted; std::tie( mapIt, inserted ) = valueToNameMap.insert( { aliasName, valueAlias.name } ); if ( inserted ) { enumValues += " " + aliasName + " = " + valueAlias.name + ",\n"; } else { // some aliases are so close to the original, that no new entry can be generated! assert( mapIt->second != valueAlias.name ); checkForError( ( mapIt->second == value.name ) || std::ranges::any_of( value.aliases, [mapIt]( auto const & eav ) { return eav.name == mapIt->second; } ), valueAlias.xmlLine, "generated enum alias value name <" + aliasName + ">, generated from <" + valueAlias.name + "> is already generated from different enum value <" + mapIt->second + ">" ); } } } previousEnter = enter; previousLeave = leave; } } enumValues += previousLeave; std::string wrapperClassComment; if ( !enumValues.empty() ) { const size_t pos = enumValues.rfind( ',' ); assert( pos != std::string::npos ); enumValues.erase( pos, 1 ); enumValues = "\n" + enumValues + " "; wrapperClassComment = "// wrapper class for enum " + enumData.first + ", see https://registry.khronos.org/vulkan/specs/latest/man/html/" + enumData.first + ".html"; } std::string enumUsing; for ( auto const & alias : enumData.second.aliases ) { enumUsing += " using " + stripPrefix( alias.first, "Vk" ) + " = " + stripPrefix( enumData.first, "Vk" ) + ";\n"; } std::string typeTraits; if ( enumData.first == "VkIndexType" ) { typeTraits = generateIndexTypeTraits( enumData ); } else if ( enumData.first == "VkLayerSettingTypeEXT" ) { typeTraits = generateLayerSettingTypeTraits(); } const std::string enumTemplate = R"( ${wrapperClassComment} enum class ${enumName}${baseType}{${enumValues}}; ${typeTraits} ${enumUsing} ${bitmask})"; return replaceWithMap( enumTemplate, { { "baseType", baseType }, { "bitmask", bitmask }, { "enumName", stripPrefix( enumData.first, "Vk" ) }, { "enumUsing", enumUsing }, { "enumValues", enumValues }, { "typeTraits", typeTraits }, { "wrapperClassComment", wrapperClassComment } } ); } std::string VulkanHppGenerator::generateEnums() const { const std::string enumsTemplate = R"( //============= //=== ENUMs === //============= ${enums} )"; std::string enums; std::set listedEnums; for ( auto const & feature : m_features ) { enums += generateEnums( feature.requireData, listedEnums, feature.name ); } for ( auto const & extension : m_extensions ) { enums += generateEnums( extension.requireData, listedEnums, extension.name ); } return replaceWithMap( enumsTemplate, { { "enums", enums } } ); } std::string VulkanHppGenerator::generateEnums( std::vector const & requireData, std::set & listedEnums, std::string const & title ) const { std::string surroundingProtect = getProtectFromTitle( title ); std::string str; for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { auto enumIt = m_enums.find( type.name ); if ( ( enumIt != m_enums.end() ) && !listedEnums.contains( type.name ) ) { listedEnums.insert( type.name ); str += "\n"; str += generateEnum( *enumIt, surroundingProtect ); } } } return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateEnumsToString() const { // start with toHexString, which is used in all the to_string functions here! const std::string enumsToStringTemplate = R"( //======================= //=== ENUMs to_string === //======================= VULKAN_HPP_INLINE std::string toHexString( uint32_t value ) { #if __cpp_lib_format return std::format( "{:x}", value ); #else std::stringstream stream; stream << std::hex << value; return stream.str(); #endif } ${enumsToString} )"; std::string enumsToString; std::set listedEnums; for ( auto const & feature : m_features ) { enumsToString += generateEnumsToString( feature.requireData, listedEnums, feature.name ); } for ( auto const & extension : m_extensions ) { enumsToString += generateEnumsToString( extension.requireData, listedEnums, extension.name ); } return replaceWithMap( enumsToStringTemplate, { { "enumsToString", enumsToString } } ); } std::string VulkanHppGenerator::generateEnumsToString( std::vector const & requireData, std::set & listedEnums, std::string const & title ) const { std::string str; for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { auto enumIt = m_enums.find( type.name ); if ( ( enumIt != m_enums.end() ) && !listedEnums.contains( type.name ) ) { listedEnums.insert( type.name ); str += "\n"; str += generateEnumToString( *enumIt ); } } } return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateEnumInitializer( TypeInfo const & type, std::vector const & arraySizes, std::vector const & values, bool bitmask ) const { // enum arguments might need special initialization assert( type.prefix.empty() && !values.empty() ); std::string valueName = generateEnumValueName( type.type, values.front().name, bitmask ); std::string value = stripPrefix( type.type, "Vk" ) + "::" + valueName; std::string str; if ( arraySizes.empty() ) { str += value; } else { assert( arraySizes.size() == 1 ); auto constIt = m_constants.find( arraySizes[0] ); const int count = std::stoi( ( constIt == m_constants.end() ) ? arraySizes[0] : constIt->second.value ); assert( 1 < count ); str += "{ { " + value; for ( int i = 1; i < count; i++ ) { str += ", " + value; } str += " } }"; } return str; } std::string VulkanHppGenerator::generateEnumToString( std::pair const & enumData ) const { std::string enumName = stripPrefix( enumData.first, "Vk" ); std::string functionBody; bool isEmpty = enumData.second.values.empty() || std::ranges::none_of( enumData.second.values, []( auto const & evd ) { return evd.supported; } ); if ( isEmpty ) { functionBody = R"x( return "(void)";)x"; } else { std::string cases, previousEnter, previousLeave; for ( auto const & value : enumData.second.values ) { if ( value.supported ) { const auto [enter, leave] = generateProtection( value.protect ); if ( previousEnter != enter ) { cases += previousLeave + enter; } const std::string caseTemplate = R"( case ${enumName}::e${valueName} : return "${valueName}"; )"; cases += replaceWithMap( caseTemplate, { { "enumName", enumName }, { "valueName", generateEnumValueName( enumData.first, value.name, enumData.second.isBitmask ).substr( 1 ) } } ); previousEnter = enter; previousLeave = leave; } } cases += previousLeave; const std::string functionBodyTemplate = R"x( switch ( value ) { ${cases} default: return "invalid ( " + toHexString( static_cast( value ) ) + " )"; } )x"; functionBody = replaceWithMap( functionBodyTemplate, { { "cases", cases } } ); } const std::string enumToStringTemplate = R"( VULKAN_HPP_INLINE VULKAN_HPP_CONSTEXPR_20 std::string to_string( ${enumName}${argument} ) { ${functionBody} } )"; return replaceWithMap( enumToStringTemplate, { { "argument", isEmpty ? "" : " value" }, { "enumName", enumName }, { "functionBody", functionBody } } ); } std::pair VulkanHppGenerator::generateEnumSuffixes( std::string const & name, bool bitmask ) const { std::string prefix, postfix; if ( name == "VkResult" ) { prefix = "VK_"; } else { if ( bitmask ) { // for a bitmask enum, start with "VK", cut off the trailing "FlagBits", and convert that name to upper case // end that with "Bit" const size_t pos = name.find( "FlagBits" ); assert( pos != std::string::npos ); std::string shortenedName = name; shortenedName.erase( pos, strlen( "FlagBits" ) ); std::string tag = findTag( shortenedName ); prefix = toUpperCase( stripPostfix( shortenedName, tag ) ) + "_"; } else { // for a non-bitmask enum, convert the name to upper case prefix = toUpperCase( name ) + "_"; } // if the enum name contains a tag move it from the prefix to the postfix to generate correct enum value // names. for ( auto const & tag : m_tags ) { if ( prefix.ends_with( tag.first + "_" ) ) { prefix.erase( prefix.length() - tag.first.length() - 1 ); postfix = "_" + tag.first; break; } else if ( name.ends_with( tag.first ) ) { postfix = "_" + tag.first; break; } } } return { prefix, postfix }; } std::string VulkanHppGenerator::generateEnumValueName( std::string const & enumName, std::string const & valueName, bool bitmask ) const { std::string prefix, postfix; std::tie( prefix, postfix ) = generateEnumSuffixes( enumName, bitmask ); std::string tag = findTag( valueName, "" ); if ( postfix == "_" + tag ) { tag = findTag( valueName, postfix ); } std::string result = "e" + toCamelCase( stripPostfix( stripPrefix( valueName, prefix ), postfix ) ); if ( bitmask ) { const size_t pos = result.rfind( "Bit" ); if ( pos != std::string::npos ) { result.erase( pos, 3 ); } } if ( !tag.empty() && ( result.substr( result.length() - tag.length() ) == toCamelCase( tag ) ) ) { result = result.substr( 0, result.length() - tag.length() ) + tag; } return result; } std::string VulkanHppGenerator::generateExtensionDependencies() const { std::string extensionDependencies, previousEnter, previousLeave; for ( auto const & extension : m_extensions ) { if ( !extension.depends.empty() ) { std::string dependsPerExtension; if ( !extensionDependencies.empty() ) { dependsPerExtension += ", "; } dependsPerExtension += "{ \"" + extension.name + "\", { "; for ( auto const & dependsByVersion : extension.depends ) { dependsPerExtension += "{ \"" + dependsByVersion.first + "\", { "; if ( !dependsByVersion.second.empty() ) { for ( auto const & depends : dependsByVersion.second ) { dependsPerExtension += "{ "; for ( auto const & dep : depends ) { dependsPerExtension += "\""s + dep + "\", "; } dependsPerExtension += " }, "; } assert( dependsPerExtension.ends_with( ", " ) ); dependsPerExtension = dependsPerExtension.substr( 0, dependsPerExtension.length() - 2 ); } dependsPerExtension += " } }, "; } assert( dependsPerExtension.ends_with( ", " ) ); dependsPerExtension = dependsPerExtension.substr( 0, dependsPerExtension.length() - 2 ); dependsPerExtension += " } }"; const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); extensionDependencies += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + dependsPerExtension; previousEnter = enter; previousLeave = leave; } } if ( !previousLeave.empty() ) { extensionDependencies += "\n" + previousLeave; } return extensionDependencies; } template std::string VulkanHppGenerator::generateExtensionReplacedBy( Predicate p, Extraction e ) const { std::string replacedBy, previousEnter, previousLeave; for ( auto const & extension : m_extensions ) { if ( p( extension ) ) { const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); replacedBy += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + " if ( extension == \"" + extension.name + "\" ) { return \"" + e( extension ) + "\"; }"; previousEnter = enter; previousLeave = leave; } } if ( !previousLeave.empty() ) { replacedBy += "\n" + previousLeave; } replacedBy += "\n return \"\";"; return replacedBy; } template std::string VulkanHppGenerator::generateExtensionReplacedTest( Predicate p ) const { std::string replacedTest, previousEnter, previousLeave; for ( auto const & extension : m_extensions ) { if ( p( extension ) ) { bool firstTest = replacedTest.empty(); const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); replacedTest += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + ( firstTest ? "" : " || " ) + "( extension == \"" + extension.name + "\" )"; previousEnter = enter; previousLeave = leave; } } if ( !previousLeave.empty() ) { // the test ends on a protected extension -> add the previousLeave and a "false" to the end // in order to make it work in case the protection is not defined. assert( !replacedTest.empty() ); replacedTest += "\n" + previousLeave + " || false"; } return replacedTest; } std::string VulkanHppGenerator::generateExtensionsList( std::string const & type ) const { std::string extensionsList, previousEnter, previousLeave; for ( auto const & extension : m_extensions ) { if ( extension.type == type ) { if ( !extensionsList.empty() ) { extensionsList += ", "; } const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); extensionsList += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + "\"" + extension.name + "\""; previousEnter = enter; previousLeave = leave; } } if ( !previousLeave.empty() ) { extensionsList += "\n" + previousLeave; } return extensionsList; } std::string VulkanHppGenerator::generateExtensionTypeTest( std::string const & type ) const { std::string typeTest, previousEnter, previousLeave; bool first = true; for ( auto const & extension : m_extensions ) { if ( extension.type == type ) { const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); typeTest += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + ( first ? "" : " || " ) + "( extension == \"" + extension.name + "\" )"; previousEnter = enter; previousLeave = leave; first = false; } } if ( !previousLeave.empty() ) { typeTest += "\n" + previousLeave; } return typeTest; } std::string VulkanHppGenerator::generateFormatTraits() const { assert( !m_formats.empty() ); auto formatIt = m_enums.find( "VkFormat" ); assert( formatIt != m_enums.end() ); assert( formatIt->second.values.front().name == "VK_FORMAT_UNDEFINED" ); auto noPredicate = []( auto const & ) { return true; }; auto generateAllFormatsList = [&]( auto const formatIt ) { return generateFormatTraitsList( formatIt->second, noPredicate ); }; auto generateAlphaCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "A"; } ); }, []( FormatData const & ) { return ""; } ); }; auto generateComponentCases = [this]( auto const & formatData, std::function generator, std::string const & defaultReturn ) { return generateFormatTraitsSubCases( formatData, []( FormatData const & formatData ) -> std::vector const & { return formatData.components; }, "component", generator, defaultReturn ); }; auto generatePlaneCases = [this]( auto const & formatData, std::function generator, std::string const & defaultReturn ) { return generateFormatTraitsSubCases( formatData, []( FormatData const & formatData ) -> std::vector const & { return formatData.planes; }, "plane", generator, defaultReturn ); }; auto generateBlockExtentCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.blockExtent.empty(); }, []( FormatData const & formatData ) { return "return {{ " + formatData.blockExtent + " }};"; } ); }; auto generateBlockSizeCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, noPredicate, []( FormatData const & formatData ) { return "return " + formatData.blockSize + ";"; } ); }; auto generateBlueCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "B"; } ); }, []( FormatData const & ) { return ""; } ); }; auto generateClassCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, noPredicate, []( FormatData const & formatData ) { return "return \"" + formatData.classAttribute + "\";"; } ); }; auto generateColorFormatsList = [&]( auto const formatIt ) { return generateFormatTraitsList( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return ( component.name == "R" ) || ( component.name == "G" ) || ( component.name == "B" ) || ( component.name == "A" ); } ); } ); }; auto generateComponentBitsCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return formatData.components.front().bits != "compressed"; }, [&]( FormatData const & formatData ) { return generateComponentCases( formatData, []( ComponentData const & componentData ) { return componentData.bits; }, "0" ); } ); }; auto generateComponentCountCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, noPredicate, []( FormatData const & formatData ) { return "return " + std::to_string( formatData.components.size() ) + ";"; } ); }; auto generateComponentNameCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, noPredicate, [&]( FormatData const & formatData ) { return generateComponentCases( formatData, []( ComponentData const & componentData ) { return "\"" + componentData.name + "\""; }, "\"\"" ); } ); }; auto generateComponentNumericFormatCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, noPredicate, [&]( FormatData const & formatData ) { return generateComponentCases( formatData, []( ComponentData const & componentData ) { return "\"" + componentData.numericFormat + "\""; }, "\"\"" ); } ); }; auto generateComponentPlaneIndexCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.components.front().planeIndex.empty(); }, [&]( FormatData const & formatData ) { return generateComponentCases( formatData, []( ComponentData const & componentData ) { return componentData.planeIndex; }, "0" ); } ); }; auto generateComponentsAreCompressedCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return formatData.components.front().bits == "compressed"; }, []( FormatData const & ) { return ""; } ); }; auto generateCompressionSchemeCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.compressed.empty(); }, []( FormatData const & formatData ) { return " return \"" + formatData.compressed + "\";"; } ); }; auto generateDepthCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "D"; } ); }, []( FormatData const & ) { return ""; } ); }; auto generateDepthFormatsList = [&]( auto const formatIt ) { return generateFormatTraitsList( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "D"; } ); } ); }; auto generateDepthStencilFormatsList = [&]( auto const formatIt ) { return generateFormatTraitsList( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "D"; } ) && std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "S"; } ); } ); }; auto generateGreenCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "G"; } ); }, []( FormatData const & ) { return ""; } ); }; auto generatePackedCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.packed.empty(); }, []( FormatData const & formatData ) { return " return " + formatData.packed + ";"; } ); }; auto generatePlaneCompatibleCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.planes.empty(); }, [&]( FormatData const & formatData ) { return generatePlaneCases( formatData, [&]( PlaneData const & planeData ) { return "Format::" + generateEnumValueName( "VkFormat", planeData.compatible, false ); }, "Format::eUndefined" ); } ); }; auto generatePlaneCountCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.planes.empty(); }, []( FormatData const & formatData ) { return " return " + std::to_string( formatData.planes.size() ) + ";"; } ); }; auto generatePlaneHeightDivisorCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.planes.empty(); }, [&]( FormatData const & formatData ) { return generatePlaneCases( formatData, [&]( PlaneData const & planeData ) { return planeData.heightDivisor; }, "1" ); } ); }; auto generatePlaneWidthDivisorCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return !formatData.planes.empty(); }, [&]( FormatData const & formatData ) { return generatePlaneCases( formatData, [&]( PlaneData const & planeData ) { return planeData.widthDivisor; }, "1" ); } ); }; auto generateRedCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "R"; } ); }, []( FormatData const & ) { return ""; } ); }; auto generateStencilCases = [this]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "S"; } ); }, []( FormatData const & ) { return ""; } ); }; auto generateStencilFormatsList = [&]( auto const formatIt ) { return generateFormatTraitsList( formatIt->second, []( FormatData const & formatData ) { return std::ranges::any_of( formatData.components, []( auto const & component ) { return component.name == "S"; } ); } ); }; auto generateTexelsPerBlockCases = [&]( auto const formatIt ) { return generateFormatTraitsCases( formatIt->second, noPredicate, []( FormatData const & formatData ) { return "return " + formatData.texelsPerBlock + ";"; } ); }; return replaceWithMap( readSnippet( "FormatTraits.hpp" ), { { "allFormats", generateAllFormatsList( formatIt ) }, { "alphaCases", generateAlphaCases( formatIt ) }, { "blockExtentCases", generateBlockExtentCases( formatIt ) }, { "blockSizeCases", generateBlockSizeCases( formatIt ) }, { "blueCases", generateBlueCases( formatIt ) }, { "classCases", generateClassCases( formatIt ) }, { "colorFormats", generateColorFormatsList( formatIt ) }, { "componentBitsCases", generateComponentBitsCases( formatIt ) }, { "componentCountCases", generateComponentCountCases( formatIt ) }, { "componentNameCases", generateComponentNameCases( formatIt ) }, { "componentNumericFormatCases", generateComponentNumericFormatCases( formatIt ) }, { "componentPlaneIndexCases", generateComponentPlaneIndexCases( formatIt ) }, { "componentsAreCompressedCases", generateComponentsAreCompressedCases( formatIt ) }, { "compressionSchemeCases", generateCompressionSchemeCases( formatIt ) }, { "depthCases", generateDepthCases( formatIt ) }, { "depthFormats", generateDepthFormatsList( formatIt ) }, { "depthStencilFormats", generateDepthStencilFormatsList( formatIt ) }, { "greenCases", generateGreenCases( formatIt ) }, { "packedCases", generatePackedCases( formatIt ) }, { "planeCompatibleCases", generatePlaneCompatibleCases( formatIt ) }, { "planeCountCases", generatePlaneCountCases( formatIt ) }, { "planeHeightDivisorCases", generatePlaneHeightDivisorCases( formatIt ) }, { "planeWidthDivisorCases", generatePlaneWidthDivisorCases( formatIt ) }, { "redCases", generateRedCases( formatIt ) }, { "stencilCases", generateStencilCases( formatIt ) }, { "stencilFormats", generateStencilFormatsList( formatIt ) }, { "texelsPerBlockCases", generateTexelsPerBlockCases( formatIt ) } } ); } std::string VulkanHppGenerator::generateFormatTraitsCases( EnumData const & enumData, std::function predicate, std::function generator ) const { std::string cases; for ( auto enumValueIt = std::next( enumData.values.begin() ); enumValueIt != enumData.values.end(); ++enumValueIt ) { if ( enumValueIt->supported ) { auto formatIt = m_formats.find( enumValueIt->name ); assert( formatIt != m_formats.end() ); if ( predicate( formatIt->second ) ) { cases += " case Format::" + generateEnumValueName( "VkFormat", formatIt->first, false ) + ": " + generator( formatIt->second ) + "\n"; } } } return cases; } std::string VulkanHppGenerator::generateFormatTraitsList( EnumData const & enumData, std::function predicate ) const { std::string list; for ( auto enumValueIt = std::next( enumData.values.begin() ); enumValueIt != enumData.values.end(); ++enumValueIt ) { if ( enumValueIt->supported ) { auto formatIt = m_formats.find( enumValueIt->name ); assert( formatIt != m_formats.end() ); if ( predicate( formatIt->second ) ) { if ( !list.empty() ) { list += ", "; } list += "Format::" + generateEnumValueName( "VkFormat", formatIt->first, false ); } } } return list; } std::string VulkanHppGenerator::generateFuncPointer( std::pair const & funcPointer, std::set & listedStructs ) const { std::string str; for ( auto const & param : funcPointer.second.params ) { auto typeIt = m_types.find( param.type.type ); assert( typeIt != m_types.end() ); if ( ( typeIt->second.category == TypeCategory::Struct ) || ( typeIt->second.category == TypeCategory::Union ) ) { auto structIt = findByNameOrAlias( m_structs, param.type.type ); assert( structIt != m_structs.end() ); if ( !listedStructs.contains( param.type.type ) ) { str += generateStruct( *structIt, listedStructs ); } } else { assert( typeIt->second.category != TypeCategory::FuncPointer ); } } const auto [enter, leave] = generateProtection( getProtectFromType( funcPointer.first ) ); std::string funcPointerParams; if ( !funcPointer.second.params.empty() ) { for ( auto const & param : funcPointer.second.params ) { if ( !funcPointerParams.empty() ) { funcPointerParams += ", "; } funcPointerParams += param.type.compose( "Vk" ) + " " + param.name; } } static const std::string funcPointerTemplate = R"( typedef ${returnType} (VKAPI_PTR *PFN_${funcPointerName})( ${funcPointerParams} ); )"; str += "\n" + enter + replaceWithMap( funcPointerTemplate, { { "funcPointerParams", funcPointerParams }, { "funcPointerName", stripPrefix( funcPointer.first, "PFN_vk" ) }, { "returnType", funcPointer.second.returnType.compose( "Vk" ) } } ) + leave; listedStructs.insert( funcPointer.first ); return str; } std::string VulkanHppGenerator::generateFuncPointerReturns() const { std::string str; std::set listedFuncPointers; for ( auto const & handle : m_handles ) { for ( auto const & command : handle.second.commands ) { auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); auto funcPointerIt = m_funcPointers.find( commandIt->second.returnType.type ); if ( ( funcPointerIt != m_funcPointers.end() ) && !listedFuncPointers.contains( commandIt->second.returnType.type ) ) { assert( funcPointerIt->second.params.empty() ); str += generateFuncPointer( *funcPointerIt, listedFuncPointers ); } } } return str; } std::string VulkanHppGenerator::generateFunctionPointerCheck( std::string const & function, std::set const & requiredBy, bool raii ) const { std::string functionPointerCheck; if ( !requiredBy.empty() ) { std::string message = "Function <" + function + "> requires <" + *requiredBy.begin() + ">"; for ( auto it = std::next( requiredBy.begin() ); it != requiredBy.end(); ++it ) { message += " or <" + *it + ">"; } functionPointerCheck = std::string( "VULKAN_HPP_ASSERT( " ) + ( raii ? "getDispatcher()->" : "d." ) + function + " && \"" + message + "\" );"; } return functionPointerCheck; } std::string VulkanHppGenerator::generateHandle( std::pair const & handleData, std::set & listedHandles ) const { assert( !listedHandles.contains( handleData.first ) ); // first check for any handle that needs to be listed before this one std::string str = generateHandleDependencies( handleData, listedHandles ); // list the commands of this handle if ( handleData.first.empty() ) { // the free functions, not bound to any handle str += generateHandleEmpty( handleData.second ); } else { // list all the commands that are mapped to members of this class std::string commands = generateHandleCommandDeclarations( handleData.second.commands ); std::string debugReportObjectType = generateDebugReportObjectType( handleData.second.objTypeEnum ); // create CPPType template specialization std::string className = stripPrefix( handleData.first, "Vk" ); std::string cppType; if ( debugReportObjectType != "eUnknown" ) { static const std::string cppTypeFromDebugReportObjectTypeEXTTemplate = R"( template <> struct CppType { using Type = ${className}; }; )"; cppType = replaceWithMap( cppTypeFromDebugReportObjectTypeEXTTemplate, { { "className", className } } ); } auto [enter, leave] = generateProtection( getProtectFromType( handleData.first ) ); assert( !handleData.second.objTypeEnum.empty() ); auto enumIt = m_enums.find( "VkObjectType" ); assert( enumIt != m_enums.end() ); assert( containsName( enumIt->second.values, handleData.second.objTypeEnum ) ); std::string usingAlias; for ( auto const & alias : handleData.second.aliases ) { usingAlias += " using " + stripPrefix( alias.first, "Vk" ) + " = " + stripPrefix( handleData.first, "Vk" ) + ";\n"; } const std::string typesafeExplicitKeyword = handleData.second.isDispatchable ? "" : "VULKAN_HPP_TYPESAFE_EXPLICIT "; const std::string typesafeConversionConditional = handleData.second.isDispatchable ? "" : "#if ( VULKAN_HPP_TYPESAFE_CONVERSION == 1 )\n"; const std::string typesafeConversionConditionalEnd = handleData.second.isDispatchable ? "" : "#endif\n"; static const std::string templateString = R"( // wrapper class for handle Vk${className}, see https://registry.khronos.org/vulkan/specs/latest/man/html/Vk${className}.html ${enter} class ${className} { public: using CType = Vk${className}; using NativeType = Vk${className}; static VULKAN_HPP_CONST_OR_CONSTEXPR ObjectType objectType = ObjectType::${objTypeEnum}; static VULKAN_HPP_CONST_OR_CONSTEXPR DebugReportObjectTypeEXT debugReportObjectType = DebugReportObjectTypeEXT::${debugReportObjectType}; public: ${className}() VULKAN_HPP_NOEXCEPT {} // = default; - try to workaround a compiler issue ${className}( ${className} const & rhs ) = default; ${className} & operator=( ${className} const & rhs ) = default; #if !defined(VULKAN_HPP_HANDLES_MOVE_EXCHANGE) ${className}( ${className} && rhs ) = default; ${className} & operator=( ${className} && rhs ) = default; #else ${className}( ${className} && rhs ) VULKAN_HPP_NOEXCEPT : m_${memberName}( exchange( rhs.m_${memberName}, {} ) ) {} ${className} & operator=( ${className} && rhs ) VULKAN_HPP_NOEXCEPT { m_${memberName} = exchange( rhs.m_${memberName}, {} ); return *this; } #endif VULKAN_HPP_CONSTEXPR ${className}( std::nullptr_t ) VULKAN_HPP_NOEXCEPT {} ${typesafeExplicitKeyword}${className}( Vk${className} ${memberName} ) VULKAN_HPP_NOEXCEPT : m_${memberName}( ${memberName} ) {} ${typesafeConversionConditional} ${className} & operator=(Vk${className} ${memberName}) VULKAN_HPP_NOEXCEPT { m_${memberName} = ${memberName}; return *this; } ${typesafeConversionConditionalEnd} ${className} & operator=( std::nullptr_t ) VULKAN_HPP_NOEXCEPT { m_${memberName} = {}; return *this; } ${commands} ${typesafeExplicitKeyword}operator Vk${className}() const VULKAN_HPP_NOEXCEPT { return m_${memberName}; } explicit operator bool() const VULKAN_HPP_NOEXCEPT { return m_${memberName} != VK_NULL_HANDLE; } bool operator!() const VULKAN_HPP_NOEXCEPT { return m_${memberName} == VK_NULL_HANDLE; } private: Vk${className} m_${memberName} = {}; }; template <> struct CppType { using Type = ${className}; }; ${CppType} #if ( VK_USE_64_BIT_PTR_DEFINES == 1 ) template <> struct CppType { using Type = ${className}; }; #endif template <> struct isVulkanHandleType<${className}> { static VULKAN_HPP_CONST_OR_CONSTEXPR bool value = true; }; ${usingAlias}${leave})"; str += replaceWithMap( templateString, { { "className", className }, { "commands", commands }, { "CppType", cppType }, { "debugReportObjectType", debugReportObjectType }, { "enter", enter }, { "leave", leave }, { "memberName", startLowerCase( stripPrefix( handleData.first, "Vk" ) ) }, { "objTypeEnum", generateEnumValueName( enumIt->first, handleData.second.objTypeEnum, false ) }, { "usingAlias", usingAlias }, { "typesafeExplicitKeyword", typesafeExplicitKeyword }, { "typesafeConversionConditional", typesafeConversionConditional }, { "typesafeConversionConditionalEnd", typesafeConversionConditionalEnd } } ); } listedHandles.insert( handleData.first ); return str; } std::string VulkanHppGenerator::generateHandleCommandDeclarations( std::set const & commands ) const { std::string str; std::set listedCommands; // some commands are listed with more than one extension! for ( auto const & feature : m_features ) { std::vector commandNames = selectCommandsByHandle( feature.requireData, commands, listedCommands ); if ( !commandNames.empty() ) { str += "\n //=== " + feature.name + " ===\n"; for ( auto const & command : commandNames ) { auto commandIt = m_commands.find( command ); assert( commandIt != m_commands.end() ); str += "\n"; str += generateCommand( commandIt->first, commandIt->second, 1, false, false ); str += generateDestroyCommand( commandIt->first, commandIt->second ); } } } for ( auto const & extension : m_extensions ) { std::vector commandNames = selectCommandsByHandle( extension.requireData, commands, listedCommands ); if ( !commandNames.empty() ) { const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); str += "\n" + enter + " //=== " + extension.name + " ===\n"; for ( auto const & command : commandNames ) { auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); std::string commandString; std::string commandName = generateCommandName( command, commandIt->second.params, 1 ); str += "\n"; str += generateCommand( command, commandIt->second, 1, false, false ); str += generateDestroyCommand( command, commandIt->second ); } str += leave; } } return str; } std::string VulkanHppGenerator::generateHandleDependencies( std::pair const & handleData, std::set & listedHandles ) const { std::string str; for ( auto const & command : handleData.second.commands ) { auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); for ( auto const & parameter : commandIt->second.params ) { auto handleIt = m_handles.find( parameter.type.type ); if ( ( handleIt != m_handles.end() ) && ( parameter.type.type != handleData.first ) && !listedHandles.contains( parameter.type.type ) ) { str += generateHandle( *handleIt, listedHandles ); } } } return str; } std::string VulkanHppGenerator::generateHandleEmpty( HandleData const & handleData ) const { std::string str; std::set listedCommands; // some commands are listed with more than one extension ! for ( auto const & feature : m_features ) { std::vector commands = selectCommandsByHandle( feature.requireData, handleData.commands, listedCommands ); if ( !commands.empty() ) { str += "\n //=== "s + feature.name + " ===\n"; for ( auto const & command : commands ) { auto commandIt = m_commands.find( command ); assert( commandIt != m_commands.end() ); str += "\n"s + generateCommand( commandIt->first, commandIt->second, 0, false, false ); } } } #if !defined( NDEBUG ) for ( auto const & extension : m_extensions ) { assert( selectCommandsByHandle( extension.requireData, handleData.commands, listedCommands ).empty() ); } #endif return str; } std::string VulkanHppGenerator::generateHandleForwardDeclarations() const { const std::string fowardDeclarationsTemplate = R"( //=================================== //=== HANDLE forward declarations === //=================================== ${forwardDeclarations} )"; std::string forwardDeclarations; for ( auto const & feature : m_features ) { forwardDeclarations += generateHandleForwardDeclarations( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { forwardDeclarations += generateHandleForwardDeclarations( extension.requireData, extension.name ); } return replaceWithMap( fowardDeclarationsTemplate, { { "forwardDeclarations", forwardDeclarations } } ); } std::string VulkanHppGenerator::generateHandleForwardDeclarations( std::vector const & requireData, std::string const & title ) const { std::string str; forEachRequiredHandle( requireData, [&str]( std::pair const & handleData ) { str += "class " + stripPrefix( handleData.first, "Vk" ) + ";\n"; } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateHandleHashStructures( std::vector const & requireData, std::string const & title, std::set & listedHandles ) const { const std::string hashTemplate = R"( template <> struct hash { std::size_t operator()(VULKAN_HPP_NAMESPACE::${type} const & ${name}) const VULKAN_HPP_NOEXCEPT { return std::hash{}(static_cast(${name})); } }; )"; std::string str; forEachRequiredHandle( requireData, [&str, &listedHandles, &hashTemplate]( std::pair const & handleData ) { if ( listedHandles.insert( handleData.first ).second ) { std::string handleType = stripPrefix( handleData.first, "Vk" ); std::string handleName = startLowerCase( handleType ); str += replaceWithMap( hashTemplate, { { "name", handleName }, { "type", handleType } } ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateHandleHashStructures() const { const std::string hashesTemplate = R"( //=================================== //=== HASH structures for handles === //=================================== ${hashes} )"; std::string hashes; std::set listedHandles; // some handles are listed with more than one extension ! for ( auto const & feature : m_features ) { hashes += generateHandleHashStructures( feature.requireData, feature.name, listedHandles ); } for ( auto const & extension : m_extensions ) { hashes += generateHandleHashStructures( extension.requireData, extension.name, listedHandles ); } return replaceWithMap( hashesTemplate, { { "hashes", hashes } } ); } std::string VulkanHppGenerator::generateHandles() const { // Note: reordering structs or handles by features and extensions is not possible! std::string str = R"( //=============== //=== HANDLEs === //=============== template struct isVulkanHandleType { static VULKAN_HPP_CONST_OR_CONSTEXPR bool value = false; }; )"; std::set listedHandles; str += generateHandle( *m_handles.begin(), listedHandles ); // artificial handle without a name! for ( auto handleIt = std::next( m_handles.begin() ); handleIt != m_handles.end(); ++handleIt ) { if ( !listedHandles.contains( handleIt->first ) && isTypeUsed( handleIt->first ) ) { str += generateHandle( *handleIt, listedHandles ); } } return str; } std::string VulkanHppGenerator::generateIndexTypeTraits( std::pair const & enumData ) const { assert( enumData.first == "VkIndexType" ); std::string typeTraits; for ( auto const & value : enumData.second.values ) { assert( value.name.starts_with( "VK_INDEX_TYPE_" ) ); std::string type = stripPrefix( value.name, "VK_INDEX_TYPE_" ); if ( !type.starts_with( "NONE" ) ) { checkForError( type.starts_with( "UINT" ), value.xmlLine, "unknown VkIndexType <" + value.name + "> encountered" ); std::string::size_type pos = type.find_first_of( "0123456789" ); assert( pos != std::string::npos ); std::string::size_type end = type.find_first_not_of( "0123456789", pos ); std::string::size_type count = ( end != std::string::npos ) ? ( end - pos ) : end; std::string valueName = generateEnumValueName( "VkIndexType", value.name, false ); std::string cppType = "uint" + type.substr( pos, count ) + "_t"; const std::string typeTraitTemplate = R"( template <> struct IndexTypeValue<${cppType}> { static VULKAN_HPP_CONST_OR_CONSTEXPR IndexType value = IndexType::${valueName}; }; template <> struct CppType { using Type = ${cppType}; }; )"; typeTraits += replaceWithMap( typeTraitTemplate, { { "cppType", cppType }, { "valueName", valueName } } ); } } const std::string typeTraitsTemplate = R"( //========================= //=== Index Type Traits === //========================= template struct IndexTypeValue {}; ${typeTraits} )"; return replaceWithMap( typeTraitsTemplate, { { "typeTraits", typeTraits } } ); } std::string VulkanHppGenerator::generateIsDispatchedList() const { const std::string isDispatchedListTemplate = R"( //========================== //=== Is Dispatched List === //========================== // C++11-compatible void_t template struct voider { typedef void type; }; template using void_t = typename voider::type; // helper macro to declare a SFINAE-friendly has_ trait # define DECLARE_IS_DISPATCHED( name ) \ template \ struct has_##name : std::false_type \ { \ }; \ template \ struct has_##name> : std::true_type \ { \ }; ${isDispatchedList} # undef DECLARE_IS_DISPATCHED # define IS_DISPATCHED( name ) ::VULKAN_HPP_NAMESPACE::detail::has_##name::value )"; std::string isDispatchedList; for ( auto const & command : m_commands ) { isDispatchedList += "DECLARE_IS_DISPATCHED( " + command.first + " )\n"; for ( auto const & alias : command.second.aliases ) { isDispatchedList += "DECLARE_IS_DISPATCHED( " + alias.first + " )\n"; } } return replaceWithMap( isDispatchedListTemplate, { { "isDispatchedList", isDispatchedList } } ); } std::string VulkanHppGenerator::generateLayerSettingTypeTraits() const { #if !defined( NDEBUG ) auto enumIt = m_enums.find( "VkLayerSettingTypeEXT" ); assert( ( enumIt != m_enums.end() ) && ( enumIt->second.values.size() == 8 ) && ( enumIt->second.values[0].name == "VK_LAYER_SETTING_TYPE_BOOL32_EXT" ) && ( enumIt->second.values[1].name == "VK_LAYER_SETTING_TYPE_INT32_EXT" ) && ( enumIt->second.values[2].name == "VK_LAYER_SETTING_TYPE_INT64_EXT" ) && ( enumIt->second.values[3].name == "VK_LAYER_SETTING_TYPE_UINT32_EXT" ) && ( enumIt->second.values[4].name == "VK_LAYER_SETTING_TYPE_UINT64_EXT" ) && ( enumIt->second.values[5].name == "VK_LAYER_SETTING_TYPE_FLOAT32_EXT" ) && ( enumIt->second.values[6].name == "VK_LAYER_SETTING_TYPE_FLOAT64_EXT" ) && ( enumIt->second.values[7].name == "VK_LAYER_SETTING_TYPE_STRING_EXT" ) ); #endif const std::string typeTraits = R"( //================================= //=== Layer Setting Type Traits === //================================= template <> struct CppType { using Type = Bool32; }; template <> struct CppType { using Type = int32_t; }; template <> struct CppType { using Type = int64_t; }; template <> struct CppType { using Type = uint32_t; }; template <> struct CppType { using Type = uint64_t; }; template <> struct CppType { using Type = float; }; template <> struct CppType { using Type = double; }; template <> struct CppType { using Type = char *; }; template bool isSameType( LayerSettingTypeEXT layerSettingType ) { switch ( layerSettingType ) { case LayerSettingTypeEXT::eBool32: return std::is_same::value; case LayerSettingTypeEXT::eInt32: return std::is_same::value; case LayerSettingTypeEXT::eInt64: return std::is_same::value; case LayerSettingTypeEXT::eUint32: return std::is_same::value; case LayerSettingTypeEXT::eUint64: return std::is_same::value; case LayerSettingTypeEXT::eFloat32: return std::is_same::value; case LayerSettingTypeEXT::eFloat64: return std::is_same::value; case LayerSettingTypeEXT::eString: return std::is_same::value; default: return false; } } )"; return typeTraits; } std::string VulkanHppGenerator::generateLenInitializer( std::vector::const_iterator mit, std::map::const_iterator, std::vector::const_iterator>>::const_iterator litit, bool mutualExclusiveLens ) const { std::string initializer; if ( mutualExclusiveLens ) { // there are multiple mutually exclusive arrays related to this len for ( size_t i = 0; i + 1 < litit->second.size(); i++ ) { auto arrayIt = litit->second[i]; std::string argumentName = startLowerCase( stripPrefix( arrayIt->name, "p" ) ) + "_"; initializer += "!" + argumentName + ".empty() ? " + argumentName + ".size() : "; } auto arrayIt = litit->second.back(); std::string argumentName = startLowerCase( stripPrefix( arrayIt->name, "p" ) ) + "_"; initializer += argumentName + ".size()"; } else { auto arrayIt = litit->second.front(); assert( ( arrayIt->lenExpressions.front() == litit->first->name ) || ( ( arrayIt->lenExpressions.front() == "codeSize / 4" ) && ( litit->first->name == "codeSize" ) ) ); assert( arrayIt->type.isPointer() || !arrayIt->arraySizes.empty() ); std::string argumentName = ( arrayIt->type.isPointer() ? startLowerCase( stripPrefix( arrayIt->name, "p" ) ) : arrayIt->name ) + "_"; assert( mit->type.prefix.empty() && mit->type.postfix.empty() ); initializer = argumentName + ".size()"; if ( arrayIt->lenExpressions.front() == "codeSize / 4" ) { initializer += " * 4"; } if ( ( arrayIt->type.type == "void" ) && ( std::count_if( arrayIt->type.postfix.begin(), arrayIt->type.postfix.end(), []( char c ) { return c == '*'; } ) < 2 ) ) { initializer += " * sizeof(T)"; } } if ( mit->type.type != "size_t" ) { initializer = "static_cast<" + mit->type.type + ">( " + initializer + " )"; } if ( !litit->second.front()->arraySizes.empty() ) { assert( litit->second.front()->arraySizes.size() == 1 ); initializer = "(std::min)( " + initializer + ", " + litit->second.front()->arraySizes[0] + " )"; } return initializer; } std::string VulkanHppGenerator::generateName( TypeInfo const & typeInfo ) const { std::string name = stripPrefix( typeInfo.type, "Vk" ); assert( typeInfo.prefix.find( '*' ) == std::string::npos ); if ( typeInfo.postfix.find( '*' ) != std::string::npos ) { assert( typeInfo.postfix.find_first_of( '*' ) == typeInfo.postfix.find_last_of( '*' ) ); name = "p" + name; } else { name = startLowerCase( name ); } return name; } std::string VulkanHppGenerator::generateNoExcept( std::vector const & errorCodes, std::vector const & returnParams, std::map const & vectorParams, CommandFlavourFlags flavourFlags, bool vectorSizeCheck, bool raii ) const { // noexcept is only possible with no error codes, and the return param (if any) is not a vector param (unless it's the singular version) return ( errorCodes.empty() && ( ( flavourFlags & CommandFlavourFlagBits::singular ) || returnParams.empty() || std::ranges::none_of( returnParams, [&vectorParams]( size_t rp ) { return vectorParams.contains( rp ); } ) ) ) ? ( vectorSizeCheck ? ( raii ? "" : " VULKAN_HPP_NOEXCEPT_WHEN_NO_EXCEPTIONS" ) : " VULKAN_HPP_NOEXCEPT" ) : ""; } std::string VulkanHppGenerator::generateObjectDeleter( std::string const & commandName, CommandData const & commandData, size_t initialSkipCount, size_t returnParam ) const { std::string objectDeleter, allocator; if ( ( commandName.find( "Acquire" ) != std::string::npos ) || ( commandName.find( "Get" ) != std::string::npos ) ) { if ( ( commandName == "vkAcquirePerformanceConfigurationINTEL" ) || ( commandName == "vkGetRandROutputDisplayEXT" ) || ( commandName == "vkGetWinrtDisplayNV" ) || ( commandName == "vkGetDrmDisplayEXT" ) ) { objectDeleter = "detail::ObjectRelease"; } else { throw std::runtime_error( "Found " + commandName + " which requires special handling for the object deleter" ); } } else if ( commandName.find( "Allocate" ) != std::string::npos ) { objectDeleter = "detail::ObjectFree"; allocator = "allocator, "; } else { assert( ( commandName.find( "Create" ) != std::string::npos ) || ( commandName.find( "Register" ) != std::string::npos ) ); objectDeleter = "detail::ObjectDestroy"; allocator = "allocator, "; } std::string className = initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : ""; std::string parentName = ( className.empty() || ( commandData.params[returnParam].type.type == "VkDevice" ) ) ? "detail::NoParent" : className; return objectDeleter + "<" + parentName + ", Dispatch>( " + ( ( parentName == "detail::NoParent" ) ? "" : "*this, " ) + allocator + "d )"; } std::string VulkanHppGenerator::generateObjectTypeToDebugReportObjectType() const { auto objectTypeToDebugReportObjectTypeTemplate = std::string{ R"( //=========================================================== //=== Mapping from ObjectType to DebugReportObjectTypeEXT === //=========================================================== VULKAN_HPP_INLINE DebugReportObjectTypeEXT debugReportObjectType( ObjectType objectType ) { switch( objectType ) { ${objectTypeCases} default: VULKAN_HPP_ASSERT( false && "unknown ObjectType" ); return DebugReportObjectTypeEXT::eUnknown; } } )" }; auto const generateObjectTypeCases = [this]( std::vector const & requireData, std::string const & title, std::set & listedTypes ) { static const std::string objectTypeCaseTemplate = " case ObjectType::${objectType} : return DebugReportObjectTypeEXT::${debugReportObjectType};\n"; std::string objectTypeCases; forEachRequiredHandle( requireData, [&objectTypeCases, &listedTypes, this]( std::pair const & handleData ) { if ( listedTypes.insert( handleData.first ).second ) { objectTypeCases += replaceWithMap( objectTypeCaseTemplate, { { "debugReportObjectType", generateDebugReportObjectType( handleData.second.objTypeEnum ) }, { "objectType", generateEnumValueName( "VkObjectType", handleData.second.objTypeEnum, false ) } } ); } } ); return addTitleAndProtection( title, objectTypeCases ); }; std::string objectTypeCases; std::set listedTypes; for ( auto const & feature : m_features ) { objectTypeCases += generateObjectTypeCases( feature.requireData, feature.name, listedTypes ); } for ( auto const & extension : m_extensions ) { objectTypeCases += generateObjectTypeCases( extension.requireData, extension.name, listedTypes ); } return replaceWithMap( objectTypeToDebugReportObjectTypeTemplate, { { "objectTypeCases", objectTypeCases } } ); } std::pair VulkanHppGenerator::generateProtection( std::string const & protect, bool defined ) const { auto const openProtect = defined ? "#if defined( " : "#if !defined( "; return protect.empty() ? std::make_pair( "", "" ) : std::make_pair( openProtect + protect + " )\n", "#endif /*" + protect + "*/\n" ); } std::string VulkanHppGenerator::generateRAIICommandDefinitions() const { const std::string commandDefinitionsTemplate = R"( //=========================== //=== COMMAND Definitions === //=========================== ${commandDefinitions} )"; std::string commandDefinitions; std::set listedCommands; // some commands are listed with more than one extension! for ( auto const & feature : m_features ) { commandDefinitions += generateRAIICommandDefinitions( feature.requireData, listedCommands, feature.name ); } for ( auto const & extension : m_extensions ) { commandDefinitions += generateRAIICommandDefinitions( extension.requireData, listedCommands, extension.name ); } return replaceWithMap( commandDefinitionsTemplate, { { "commandDefinitions", commandDefinitions } } ); } std::string VulkanHppGenerator::generateRAIICommandDefinitions( std::vector const & requireData, std::set & listedCommands, std::string const & title ) const { std::string str; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & ) { if ( listedCommands.insert( command.name ).second ) { str += generateRAIIHandleCommand( command.name, determineInitialSkipCount( command.name ), true ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateRAIIDispatcherCommandsContextInitializers( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string contextInitializers; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && commandData.second.handle.empty() ) { contextInitializers += ", " + command.name + "( PFN_" + command.name + "( getProcAddr( NULL, \"" + command.name + "\" ) ) )"; } } ); return addTitleAndProtection( title, contextInitializers ); } std::string VulkanHppGenerator::generateRAIIDispatcherCommandsContextMembers( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string contextMembers; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && commandData.second.handle.empty() ) { contextMembers += " PFN_" + command.name + " " + command.name + " = 0;\n"; } } ); return addTitleAndProtection( title, contextMembers ); } std::string VulkanHppGenerator::generateRAIIDispatcherCommandsDeviceAssignments( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string deviceAssignments; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && ( ( commandData.second.handle == "VkDevice" ) || hasParentHandle( commandData.second.handle, "VkDevice" ) ) ) { deviceAssignments += " " + command.name + " = PFN_" + command.name + "( vkGetDeviceProcAddr( device, \"" + command.name + "\" ) );\n"; // if this is an alias'ed function, use it as a fallback for the original one if ( command.name != commandData.first ) { assert( commandData.second.aliases.contains( command.name ) ); deviceAssignments += " if ( !" + commandData.first + " ) " + commandData.first + " = " + command.name + ";\n"; } } } ); return addTitleAndProtection( title, deviceAssignments ); } std::string VulkanHppGenerator::generateRAIIDispatcherCommandsDeviceMembers( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string deviceMembers, deviceMemberDummies; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && ( ( commandData.second.handle == "VkDevice" ) || hasParentHandle( commandData.second.handle, "VkDevice" ) ) ) { deviceMembers += " PFN_" + command.name + " " + command.name + " = 0;\n"; deviceMemberDummies += " PFN_dummy " + command.name + "_placeholder = 0;\n"; } } ); return addTitleAndProtection( title, deviceMembers, deviceMemberDummies ); } std::string VulkanHppGenerator::generateRAIIDispatcherCommandsInstanceAssignments( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string instanceAssignments; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && !commandData.second.handle.empty() && !( ( commandData.second.handle == "VkDevice" ) || hasParentHandle( commandData.second.handle, "VkDevice" ) ) ) { assert( ( commandData.second.handle == "VkInstance" ) || hasParentHandle( commandData.second.handle, "VkInstance" ) ); // filter out vkGetInstanceProcAddr, as starting with Vulkan 1.2 it can resolve itself only (!) with an // instance nullptr ! if ( command.name != "vkGetInstanceProcAddr" ) { instanceAssignments += " " + command.name + " = PFN_" + command.name + "( vkGetInstanceProcAddr( instance, \"" + command.name + "\" ) );\n"; // if this is an alias'ed function, use it as a fallback for the original one if ( command.name != commandData.first ) { assert( commandData.second.aliases.contains( command.name ) ); instanceAssignments += " if ( !" + commandData.first + " ) " + commandData.first + " = " + command.name + ";\n"; } } } } ); return addTitleAndProtection( title, instanceAssignments ); } std::string VulkanHppGenerator::generateRAIIDispatcherCommandsInstanceMembers( std::vector const & requireData, std::set const & listedCommands, std::string const & title ) const { std::string instanceMembers, instanceMemberDummies; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & commandData ) { if ( !listedCommands.contains( command.name ) && !commandData.second.handle.empty() && !( ( commandData.second.handle == "VkDevice" ) || hasParentHandle( commandData.second.handle, "VkDevice" ) ) ) { assert( ( commandData.second.handle == "VkInstance" ) || hasParentHandle( commandData.second.handle, "VkInstance" ) ); instanceMembers += +" PFN_" + command.name + " " + command.name + " = 0;\n"; instanceMemberDummies += " PFN_dummy " + command.name + "_placeholder = 0;\n"; } } ); return addTitleAndProtection( title, instanceMembers, instanceMemberDummies ); } std::string VulkanHppGenerator::generateRAIIDispatchers() const { std::string contextInitializers, contextMembers, deviceAssignments, deviceMembers, instanceAssignments, instanceMembers; std::set listedCommands; for ( auto const & feature : m_features ) { contextInitializers += generateRAIIDispatcherCommandsContextInitializers( feature.requireData, listedCommands, feature.name ); contextMembers += generateRAIIDispatcherCommandsContextMembers( feature.requireData, listedCommands, feature.name ); deviceAssignments += generateRAIIDispatcherCommandsDeviceAssignments( feature.requireData, listedCommands, feature.name ); deviceMembers += generateRAIIDispatcherCommandsDeviceMembers( feature.requireData, listedCommands, feature.name ); instanceAssignments += generateRAIIDispatcherCommandsInstanceAssignments( feature.requireData, listedCommands, feature.name ); instanceMembers += generateRAIIDispatcherCommandsInstanceMembers( feature.requireData, listedCommands, feature.name ); forEachRequiredCommand( feature.requireData, [&listedCommands]( NameLine const & command, auto const & ) { listedCommands.insert( command.name ); } ); } for ( auto const & extension : m_extensions ) { contextInitializers += generateRAIIDispatcherCommandsContextInitializers( extension.requireData, listedCommands, extension.name ); contextMembers += generateRAIIDispatcherCommandsContextMembers( extension.requireData, listedCommands, extension.name ); deviceAssignments += generateRAIIDispatcherCommandsDeviceAssignments( extension.requireData, listedCommands, extension.name ); deviceMembers += generateRAIIDispatcherCommandsDeviceMembers( extension.requireData, listedCommands, extension.name ); instanceAssignments += generateRAIIDispatcherCommandsInstanceAssignments( extension.requireData, listedCommands, extension.name ); instanceMembers += generateRAIIDispatcherCommandsInstanceMembers( extension.requireData, listedCommands, extension.name ); forEachRequiredCommand( extension.requireData, [&listedCommands]( NameLine const & command, auto const & ) { listedCommands.insert( command.name ); } ); } std::string contextDispatcherTemplate = R"( class ContextDispatcher : public ::VULKAN_HPP_NAMESPACE::detail::DispatchLoaderBase { public: ContextDispatcher( PFN_vkGetInstanceProcAddr getProcAddr ) : vkGetInstanceProcAddr( getProcAddr )${contextInitializers} {} public: PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 0; ${contextMembers} }; )"; std::string str = replaceWithMap( contextDispatcherTemplate, { { "contextInitializers", contextInitializers }, { "contextMembers", contextMembers } } ); std::string instanceDispatcherTemplate = R"( class InstanceDispatcher : public ::VULKAN_HPP_NAMESPACE::detail::DispatchLoaderBase { public: InstanceDispatcher( PFN_vkGetInstanceProcAddr getProcAddr, VkInstance instance ) : vkGetInstanceProcAddr( getProcAddr ) { ${instanceAssignments} vkGetDeviceProcAddr = PFN_vkGetDeviceProcAddr( vkGetInstanceProcAddr( instance, "vkGetDeviceProcAddr" ) ); } public: ${instanceMembers} PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = 0; }; )"; str += replaceWithMap( instanceDispatcherTemplate, { { "instanceAssignments", instanceAssignments }, { "instanceMembers", instanceMembers } } ); std::string deviceDispatcherTemplate = R"( class DeviceDispatcher : public ::VULKAN_HPP_NAMESPACE::detail::DispatchLoaderBase { public: DeviceDispatcher( PFN_vkGetDeviceProcAddr getProcAddr, VkDevice device ) : vkGetDeviceProcAddr( getProcAddr ) { ${deviceAssignments} } public: ${deviceMembers} }; )"; str += replaceWithMap( deviceDispatcherTemplate, { { "deviceAssignments", deviceAssignments }, { "deviceMembers", deviceMembers } } ); return str; } std::string VulkanHppGenerator::generateRAIIFactoryReturnStatements( CommandData const & commandData, std::string const & vkType, bool enumerating, std::string const & returnType, std::string const & returnVariable, bool singular ) const { auto handleIt = findByNameOrAlias( m_handles, vkType ); assert( handleIt != m_handles.end() ); std::string successCodePassToElement = ( enumerating ? ( commandData.successCodes.size() <= 2 ) : ( commandData.successCodes.size() <= 1 ) ) ? "" : ", result"; if ( returnType.starts_with( "std::vector" ) ) { assert( !commandData.successCodes.empty() ); std::string const & returnTemplate = R"(${returnType} ${returnVariable}RAII; if ( result == Result::eSuccess ) { ${returnVariable}RAII.reserve( ${returnVariable}.size() ); for ( auto & ${element} : ${returnVariable} ) { ${returnVariable}RAII.emplace_back( *this, ${handleConstructorArguments}${successCodePassToElement} ); } } return VULKAN_HPP_NAMESPACE::detail::createResultValueType( result, std::move( ${returnVariable}RAII ) ); )"; std::string element = stripPluralS( returnVariable ); std::string handleConstructorArguments = generateRAIIHandleSingularConstructorArguments( *handleIt, commandData.params, vkType, element ); return replaceWithMap( returnTemplate, { { "element", element }, { "handleConstructorArguments", handleConstructorArguments }, { "successCodePassToElement", successCodePassToElement }, { "returnType", returnType }, { "returnVariable", returnVariable } } ); } else { std::string const & returnTypeTemplate = "${returnType}( *this, ${handleConstructorArguments}${successCodePassToElement} )"; std::string handleConstructorArguments = generateRAIIHandleSingularConstructorArguments( *handleIt, commandData.params, vkType, singular ? stripPluralS( returnVariable ) : returnVariable ); std::string constructorCall = replaceWithMap( returnTypeTemplate, { { "returnType", returnType }, { "handleConstructorArguments", handleConstructorArguments }, { "successCodePassToElement", successCodePassToElement } } ); if ( commandData.successCodes.empty() && commandData.errorCodes.empty() ) { return "return " + constructorCall + ";"; } else { return "return VULKAN_HPP_NAMESPACE::detail::createResultValueType( result, " + constructorCall + " );"; } } } std::string VulkanHppGenerator::generateRAIIHandle( std::pair const & handle, std::set & listedHandles, std::set const & specialFunctions ) const { std::string str; if ( !listedHandles.contains( handle.first ) ) { rescheduleRAIIHandle( str, handle, listedHandles, specialFunctions ); auto [enter, leave] = generateProtection( getProtectFromType( handle.first ) ); std::string handleType = stripPrefix( handle.first, "Vk" ); std::string handleName = startLowerCase( stripPrefix( handle.first, "Vk" ) ); auto [singularConstructors, arrayConstructors] = generateRAIIHandleConstructors( handle ); auto [clearMembers, getConstructorSuccessCode, memberVariables, rawConstructorParams, rawConstructorInitializerList, moveConstructorInitializerList, moveAssignmentInstructions, swapMembers, releaseMembers] = generateRAIIHandleDetails( handle ); std::string declarations = generateRAIIHandleCommandDeclarations( handle, specialFunctions ); assert( !handle.second.objTypeEnum.empty() ); auto enumIt = m_enums.find( "VkObjectType" ); assert( enumIt != m_enums.end() ); assert( containsName( enumIt->second.values, handle.second.objTypeEnum ) ); std::string objTypeEnum = generateEnumValueName( enumIt->first, handle.second.objTypeEnum, false ); enumIt = m_enums.find( "VkDebugReportObjectTypeEXT" ); assert( enumIt != m_enums.end() ); std::string valueName = handle.second.objTypeEnum; valueName = valueName.replace( 3, 0, "DEBUG_REPORT_" ) + "_EXT"; std::string debugReportObjectType = containsName( enumIt->second.values, valueName ) ? generateEnumValueName( enumIt->first, valueName, false ) : "eUnknown"; std::string dispatcherType = ( ( handle.first == "VkDevice" ) || ( handle.second.constructorIts.front()->second.params.front().type.type == "VkDevice" ) ) ? "detail::DeviceDispatcher" : "detail::InstanceDispatcher"; std::string getParent; if ( ( handle.first != "VkInstance" ) && ( handle.first != "VkDevice" ) && ( handle.second.destructorIt != m_commands.end() ) ) { assert( !handle.second.destructorIt->second.params.empty() ); std::string parentType = stripPrefix( handle.second.destructorIt->second.params.front().type.type, "Vk" ); getParent = " VULKAN_HPP_NAMESPACE::" + parentType + " get" + parentType + "() const\n"; getParent += " {\n"; getParent += " return m_" + handle.second.destructorIt->second.params.front().name + ";\n"; getParent += " }\n"; } std::string assignmentOperator, copyConstructor; if ( handle.second.destructorIt == m_commands.end() ) { // allow copy constructor and assignment operator for classes without destructor std::string const copyConstructorTemplate = R"( ${handleType}( ${handleType} const & rhs ) : m_${handleName}( rhs.m_${handleName} ), m_dispatcher( rhs.m_dispatcher ) {})"; copyConstructor += replaceWithMap( copyConstructorTemplate, { { "handleName", handleName }, { "handleType", handleType } } ); std::string assignmentOperatorTemplate = R"( ${handleType} & operator=( ${handleType} const & rhs ) { m_${handleName} = rhs.m_${handleName}; m_dispatcher = rhs.m_dispatcher; return *this; })"; assignmentOperator += replaceWithMap( assignmentOperatorTemplate, { { "handleName", handleName }, { "handleType", handleType } } ); } else { std::string const copyConstructorTemplate = R"( ${handleType}( ${handleType} const & ) = delete;)"; copyConstructor += replaceWithMap( copyConstructorTemplate, { { "handleType", handleType } } ); std::string const assignmentOperatorTemplate = R"( ${handleType} & operator=( ${handleType} const & ) = delete;)"; assignmentOperator += replaceWithMap( assignmentOperatorTemplate, { { "handleType", handleType } } ); } std::string rawConstructor; if ( ( handle.first == "VkBuffer" ) || ( handle.first == "VkImage" ) ) { std::string const rawConstructorTemplate = R"(protected: ${handleType}( ${rawConstructorParams} ) VULKAN_HPP_NOEXCEPT : ${rawConstructorInitializerList} {})"; rawConstructor += replaceWithMap( rawConstructorTemplate, { { "handleType", handleType }, { "rawConstructorParams", rawConstructorParams }, { "rawConstructorInitializerList", rawConstructorInitializerList } } ); } const std::string handleTemplate = R"( // wrapper class for handle Vk${handleType}, see https://registry.khronos.org/vulkan/specs/latest/man/html/Vk${handleType}.html ${enter} class ${handleType} { public: using CType = Vk${handleType}; using CppType = VULKAN_HPP_NAMESPACE::${handleType}; static VULKAN_HPP_CONST_OR_CONSTEXPR ObjectType objectType = ObjectType::${objTypeEnum}; static VULKAN_HPP_CONST_OR_CONSTEXPR DebugReportObjectTypeEXT debugReportObjectType = DebugReportObjectTypeEXT::${debugReportObjectType}; public: ${singularConstructors} ${handleType}( std::nullptr_t ) {} ~${handleType}() { clear(); } ${handleType}() = delete; ${copyConstructor} ${handleType}( ${handleType} && rhs ) VULKAN_HPP_NOEXCEPT : ${moveConstructorInitializerList} {} ${assignmentOperator} ${handleType} & operator=( ${handleType} && rhs ) VULKAN_HPP_NOEXCEPT { if ( this != &rhs ) { ${moveAssignmentInstructions} } return *this; } VULKAN_HPP_NAMESPACE::${handleType} const & operator*() const & VULKAN_HPP_NOEXCEPT { return m_${handleName}; } VULKAN_HPP_NAMESPACE::${handleType} const && operator*() const && VULKAN_HPP_NOEXCEPT { return std::move(m_${handleName}); } operator VULKAN_HPP_NAMESPACE::${handleType}() const VULKAN_HPP_NOEXCEPT { return m_${handleName}; } void clear() VULKAN_HPP_NOEXCEPT { ${clearMembers} } VULKAN_HPP_NAMESPACE::${handleType} release() { ${releaseMembers} } ${getConstructorSuccessCode} ${getParent} ${dispatcherType} const * getDispatcher() const { VULKAN_HPP_ASSERT( m_dispatcher->getVkHeaderVersion() == VK_HEADER_VERSION ); return ${getDispatcherReturn}m_dispatcher; } void swap( ${handleType} & rhs ) VULKAN_HPP_NOEXCEPT { ${swapMembers} } ${memberFunctionsDeclarations} ${rawConstructor} private: ${memberVariables} }; template <> struct isVulkanRAIIHandleType<${handleType}> { static VULKAN_HPP_CONST_OR_CONSTEXPR bool value = true; }; ${leave})"; str += replaceWithMap( handleTemplate, { { "assignmentOperator", assignmentOperator }, { "clearMembers", clearMembers }, { "copyConstructor", copyConstructor }, { "debugReportObjectType", debugReportObjectType }, { "dispatcherType", dispatcherType }, { "enter", enter }, { "getConstructorSuccessCode", getConstructorSuccessCode }, { "getDispatcherReturn", ( handleType == "Device" ) || ( handleType == "Instance" ) ? "&*" : "" }, { "getParent", getParent }, { "handleName", handleName }, { "handleType", handleType }, { "leave", leave }, { "memberFunctionsDeclarations", declarations }, { "rawConstructor", rawConstructor }, { "memberVariables", memberVariables }, { "moveAssignmentInstructions", moveAssignmentInstructions }, { "moveConstructorInitializerList", moveConstructorInitializerList }, { "objTypeEnum", objTypeEnum }, { "releaseMembers", releaseMembers }, { "singularConstructors", singularConstructors }, { "swapMembers", swapMembers } } ); if ( !arrayConstructors.empty() ) { // it's a handle class with a friendly handles class const std::string handlesTemplate = R"( ${enter} class ${handleType}s : public std::vector<${handleType}> { public: ${arrayConstructors} ${handleType}s( std::nullptr_t ) {} ${handleType}s() = delete; ${handleType}s( ${handleType}s const & ) = delete; ${handleType}s( ${handleType}s && rhs ) = default; ${handleType}s & operator=( ${handleType}s const & ) = delete; ${handleType}s & operator=( ${handleType}s && rhs ) = default; ${handleType}s( std::vector<${handleType}> && rhs ) { std::swap( *this, rhs ); } }; ${leave} )"; str += replaceWithMap( handlesTemplate, { { "arrayConstructors", arrayConstructors }, { "enter", enter }, { "handleType", handleType }, { "leave", leave } } ); } } return str; } std::string VulkanHppGenerator::generateRAIIHandleCommand( std::string const & command, size_t initialSkipCount, bool definition ) const { std::string str; if ( !m_RAIISpecialFunctions.contains( command ) ) { auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); str = generateCommand( command, commandIt->second, initialSkipCount, definition, true ); } return str; } std::string VulkanHppGenerator::generateRAIIHandleCommandDeclarations( std::pair const & handle, std::set const & specialFunctions ) const { std::string functionDeclarations; std::map listedCommands; // some commands are listed with more than one extension ! for ( auto const & feature : m_features ) { std::vector firstLevelCommands, secondLevelCommands; forEachRequiredCommand( feature.requireData, [&]( NameLine const & command, auto const & ) { if ( !specialFunctions.contains( command.name ) ) { if ( handle.second.commands.contains( command.name ) ) { assert( !listedCommands.contains( command.name ) ); listedCommands.insert( { command.name, { feature.name, command.xmlLine } } ); firstLevelCommands.push_back( command.name ); } else if ( handle.second.secondLevelCommands.contains( command.name ) ) { auto listedIt = listedCommands.find( command.name ); if ( listedIt == listedCommands.end() ) { listedCommands.insert( { command.name, { feature.name, command.xmlLine } } ); assert( !handle.first.empty() ); secondLevelCommands.push_back( command.name ); } else { checkForError( listedIt->second.name == feature.name, command.xmlLine, "command <" + command.name + "> already listed as required for feature <" + listedIt->second.name + "> on line " + std::to_string( listedIt->second.xmlLine ) ); } } } } ); if ( !firstLevelCommands.empty() || !secondLevelCommands.empty() ) { functionDeclarations += "\n //=== " + feature.name + " ===\n"; for ( auto const & command : firstLevelCommands ) { functionDeclarations += generateRAIIHandleCommand( command, handle.first.empty() ? 0 : 1, false ); } for ( auto const & command : secondLevelCommands ) { assert( !handle.first.empty() ); functionDeclarations += generateRAIIHandleCommand( command, 2, false ); } } } for ( auto const & extension : m_extensions ) { std::vector firstLevelCommands, secondLevelCommands; for ( auto & req : extension.requireData ) { for ( auto const & command : req.commands ) { if ( !specialFunctions.contains( command.name ) && !listedCommands.contains( command.name ) ) { if ( handle.second.commands.contains( command.name ) ) { listedCommands.insert( { command.name, { extension.name, command.xmlLine } } ); firstLevelCommands.push_back( command.name ); } else if ( handle.second.secondLevelCommands.contains( command.name ) ) { listedCommands.insert( { command.name, { extension.name, command.xmlLine } } ); secondLevelCommands.push_back( command.name ); } } } } if ( !firstLevelCommands.empty() || !secondLevelCommands.empty() ) { std::string handleProtect = getProtectFromType( handle.first ); std::string titleProtect = getProtectFromTitle( extension.name ); std::string enter, leave; if ( !titleProtect.empty() && ( titleProtect != handleProtect ) ) { std::tie( enter, leave ) = generateProtection( titleProtect ); } functionDeclarations += "\n" + enter + " //=== " + extension.name + " ===\n"; for ( auto const & command : firstLevelCommands ) { functionDeclarations += generateRAIIHandleCommand( command, handle.first.empty() ? 0 : 1, false ); } for ( auto const & command : secondLevelCommands ) { assert( !handle.first.empty() ); functionDeclarations += generateRAIIHandleCommand( command, 2, false ); } functionDeclarations += leave; } } return functionDeclarations; } std::string VulkanHppGenerator::generateRAIIHandleCommandEnhanced( std::string const & name, CommandData const & commandData, size_t initialSkipCount, std::vector const & returnParams, std::map const & vectorParams, bool definition, CommandFlavourFlags flavourFlags ) const { const bool singular = flavourFlags & CommandFlavourFlagBits::singular; std::set skippedParams = determineSkippedParams( commandData.params, initialSkipCount, vectorParams, returnParams, singular ); std::set singularParams = singular ? determineSingularParams( returnParams[0], vectorParams ) : std::set(); std::set templatedParams = ( flavourFlags & CommandFlavourFlagBits::keepVoidPtr ) ? std::set() : determineVoidPointerParams( commandData.params ); std::vector chainedReturnParams = ( flavourFlags & CommandFlavourFlagBits::chained ) ? determineChainedReturnParams( commandData.params, returnParams ) : std::vector(); assert( chainedReturnParams.size() <= 1 ); const bool enumerating = determineEnumeration( vectorParams, returnParams ); std::vector dataTypes = determineDataTypes( commandData.params, vectorParams, returnParams, templatedParams, true ); std::string returnDataType = generateReturnDataType( vectorParams, returnParams, enumerating, dataTypes, flavourFlags, true ); std::string argumentTemplates = generateArgumentTemplates( commandData.params, returnParams, vectorParams, templatedParams, chainedReturnParams, true ); std::string argumentList = generateArgumentListEnhanced( commandData.params, returnParams, vectorParams, skippedParams, singularParams, templatedParams, chainedReturnParams, true, definition, flavourFlags, false ); std::string commandName = generateCommandName( name, commandData.params, initialSkipCount, flavourFlags ); std::string nodiscard = generateNoDiscard( !returnParams.empty() || ( ( commandData.returnType.type != "VkResult" ) && ( commandData.returnType.type != "void" ) ), 1 < commandData.successCodes.size(), false ); std::pair>> vectorSizeCheck = needsVectorSizeCheck( commandData.params, vectorParams, returnParams, singularParams, skippedParams ); std::string noexceptString = generateNoExcept( commandData.errorCodes, returnParams, vectorParams, flavourFlags, vectorSizeCheck.first, true ); std::string returnType = generateReturnType( returnParams, vectorParams, flavourFlags, true, dataTypes ); std::string decoratedReturnType = generateDecoratedReturnType( commandData, returnParams, vectorParams, enumerating, flavourFlags, returnType ); if ( definition ) { std::string const definitionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html ${argumentTemplates} ${nodiscard} VULKAN_HPP_INLINE ${returnType} ${className}::${commandName}( ${argumentList} ) const ${noexcept} { ${functionPointerCheck} ${vectorSizeCheck} ${dataSizeChecks} ${dataDeclarations} ${callSequence} ${resultCheck} ${dataPreparation} ${returnStatement} } )"; std::string callSequence = generateCallSequence( name, commandData, returnParams, vectorParams, initialSkipCount, singularParams, templatedParams, chainedReturnParams, flavourFlags, true, false ); std::string className = initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : "Context"; std::string returnVariable = generateReturnVariable( commandData, returnParams, vectorParams, flavourFlags ); std::string dataDeclarations = generateDataDeclarations( commandData, returnParams, vectorParams, templatedParams, flavourFlags, true, dataTypes, returnDataType, returnType, returnVariable ); std::string dataPreparation = generateDataPreparation( commandData, initialSkipCount, returnParams, vectorParams, templatedParams, flavourFlags, enumerating, dataTypes ); std::string dataSizeChecks = generateDataSizeChecks( commandData, returnParams, dataTypes, vectorParams, templatedParams, singular ); std::string resultCheck = generateResultCheck( commandData, className, "::", commandName, enumerating, ( returnParams.size() == 1 ) && ( commandData.params[returnParams[0]].type.type == "VkPipeline" ) && !singular, true ); std::string returnStatement = generateReturnStatement( name, commandData, returnVariable, returnType, decoratedReturnType, returnDataType, initialSkipCount, returnParams.empty() ? INVALID_INDEX : returnParams[0], flavourFlags, enumerating, true ); std::string vectorSizeCheckString = vectorSizeCheck.first ? generateVectorSizeCheck( name, commandData, initialSkipCount, vectorSizeCheck.second, skippedParams, false ) : ""; return replaceWithMap( definitionTemplate, { { "argumentList", argumentList }, { "argumentTemplates", argumentTemplates }, { "callSequence", callSequence }, { "className", className }, { "commandName", commandName }, { "dataDeclarations", dataDeclarations }, { "dataPreparation", dataPreparation }, { "dataSizeChecks", dataSizeChecks }, { "functionPointerCheck", generateFunctionPointerCheck( name, commandData.requiredBy, true ) }, { "nodiscard", nodiscard }, { "noexcept", noexceptString }, { "resultCheck", resultCheck }, { "returnStatement", returnStatement }, { "returnType", decoratedReturnType }, { "vectorSizeCheck", vectorSizeCheckString }, { "vkCommandName", name } } ); } else { std::string const declarationTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html ${argumentTemplates} ${nodiscard} ${returnType} ${commandName}( ${argumentList} ) const ${noexcept}; )"; return replaceWithMap( declarationTemplate, { { "argumentList", argumentList }, { "argumentTemplates", argumentTemplates }, { "commandName", commandName }, { "nodiscard", nodiscard }, { "noexcept", noexceptString }, { "returnType", decoratedReturnType }, { "vkCommandName", name } } ); } } std::string VulkanHppGenerator::generateRAIIHandleCommandFactory( std::string const & name, CommandData const & commandData, size_t initialSkipCount, std::vector const & returnParams, std::map const & vectorParams, bool definition, CommandFlavourFlags flavourFlags ) const { assert( isHandleType( commandData.params[returnParams.back()].type.type ) || ( vectorParams.contains( returnParams.back() ) && vectorParams.find( returnParams.back() )->second.byStructure && isHandleType( vectorMemberByStructure( commandData.params[returnParams.back()].type.type ).type.type ) ) ); assert( ( returnParams.size() == 1 ) || ( ( returnParams.size() == 2 ) && ( vectorParams.size() == 1 ) && ( returnParams[0] == vectorParams.begin()->second.lenParam ) && ( returnParams[1] == vectorParams.begin()->first ) ) ); assert( determineVoidPointerParams( commandData.params ).empty() ); const bool enumerating = determineEnumeration( vectorParams, returnParams ); const bool singular = flavourFlags & CommandFlavourFlagBits::singular; std::set skippedParams = determineSkippedParams( commandData.params, initialSkipCount, vectorParams, returnParams, singular ); std::set singularParams = singular ? determineSingularParams( returnParams.back(), vectorParams ) : std::set(); std::string argumentList = generateRAIIHandleCommandFactoryArgumentList( commandData.params, skippedParams, definition, singular ); std::string commandName = generateCommandName( name, commandData.params, initialSkipCount, flavourFlags ); std::string handleType; if ( ( vectorParams.size() == 1 ) && vectorParams.begin()->second.byStructure ) { assert( vectorParams.begin()->first == returnParams.back() ); handleType = vectorMemberByStructure( commandData.params.back().type.type ).type.type; } else { handleType = commandData.params[returnParams.back()].type.type; } auto handleIt = findByNameOrAlias( m_handles, handleType ); assert( handleIt != m_handles.end() ); // in case the handle to create is an alias of an other handle, use the other handle if ( handleIt->first != handleType ) { handleType = handleIt->first; } handleType = stripPrefix( handleType, "Vk" ); std::string noexceptString = enumerating ? "" : "VULKAN_HPP_NOEXCEPT_WHEN_NO_EXCEPTIONS"; std::string returnType = handleType; if ( vectorParams.contains( returnParams.back() ) && !singular ) { noexceptString = ""; returnType = "std::vector<" + handleType + ">"; handleType += "s"; } std::string decoratedReturnType = returnType; if ( !commandData.errorCodes.empty() ) { decoratedReturnType = "typename ResultValueType<" + returnType + ">::type"; } if ( definition ) { std::string className = initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : "Context"; std::vector dataTypes = determineDataTypes( commandData.params, vectorParams, returnParams, {}, true ); std::string returnDataType = generateReturnDataType( vectorParams, returnParams, enumerating, dataTypes, flavourFlags, true ); std::string returnVariable = generateReturnVariable( commandData, returnParams, vectorParams, flavourFlags ); std::string vulkanType; auto vectorParamIt = vectorParams.find( returnParams.back() ); if ( ( vectorParamIt != vectorParams.end() ) && vectorParamIt->second.byStructure ) { vulkanType = vectorMemberByStructure( commandData.params[returnParams.back()].type.type ).type.type; } else { vulkanType = commandData.params[returnParams.back()].type.type; } std::string dataDeclarations = generateDataDeclarations( commandData, returnParams, vectorParams, {}, flavourFlags, true, dataTypes, returnDataType, returnType, returnVariable ); std::string callSequence = generateCallSequence( name, commandData, returnParams, vectorParams, initialSkipCount, singularParams, {}, {}, flavourFlags, true, true ); std::string resultCheck = generateResultCheck( commandData, className, "::", commandName, enumerating, false, true ); std::string returnStatements = generateRAIIFactoryReturnStatements( commandData, vulkanType, enumerating, returnType, returnVariable, singular ); std::string const definitionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html VULKAN_HPP_NODISCARD VULKAN_HPP_INLINE ${returnType} ${className}::${commandName}( ${argumentList} ) const ${noexcept} { ${functionPointerCheck} ${dataDeclarations} ${callSequence} ${resultCheck} ${returnStatements} } )"; return replaceWithMap( definitionTemplate, { { "argumentList", argumentList }, { "callSequence", callSequence }, { "className", className }, { "commandName", commandName }, { "dataDeclarations", dataDeclarations }, { "functionPointerCheck", generateFunctionPointerCheck( name, commandData.requiredBy, true ) }, { "noexcept", noexceptString }, { "resultCheck", resultCheck }, { "returnStatements", returnStatements }, { "returnType", decoratedReturnType }, { "vkCommandName", name } } ); } else { std::string const declarationTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html VULKAN_HPP_NODISCARD ${returnType} ${commandName}( ${argumentList} ) const ${noexcept}; )"; return replaceWithMap( declarationTemplate, { { "argumentList", argumentList }, { "commandName", commandName }, { "noexcept", noexceptString }, { "returnType", decoratedReturnType }, { "vkCommandName", name } } ); } } std::string VulkanHppGenerator::generateRAIIHandleCommandFactoryArgumentList( std::vector const & params, std::set const & skippedParams, bool definition, bool singular ) const { std::vector arguments; for ( size_t i = 0; i < params.size(); ++i ) { if ( !skippedParams.contains( i ) ) { arguments.push_back( generateRAIIHandleConstructorArgument( params[i], definition, singular, false ) ); } } return generateList( arguments, "", ", " ); } std::string VulkanHppGenerator::generateRAIIHandleCommandStandard( std::string const & name, CommandData const & commandData, size_t initialSkipCount, bool definition, bool raii ) const { std::set skippedParams = determineSkippedParams( commandData.params, initialSkipCount, {}, {}, false ); std::string argumentList = generateArgumentListStandard( commandData.params, skippedParams, definition, raii, false ); std::string commandName = generateCommandName( name, commandData.params, initialSkipCount ); std::string nodiscard = ( commandData.returnType.type != "void" ) ? "VULKAN_HPP_NODISCARD" : ""; std::string returnType = stripPrefix( commandData.returnType.type, "Vk" ); if ( definition ) { std::string functionBody = "getDispatcher()->" + name + "( " + generateCallArgumentsStandard( commandData.params, initialSkipCount ) + " )"; if ( commandData.returnType.type.starts_with( "Vk" ) ) { functionBody = "return static_cast<" + returnType + ">( " + functionBody + " )"; } else if ( commandData.returnType.type != "void" ) { functionBody = "return " + functionBody; } std::string const functionTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html ${nodiscard} VULKAN_HPP_INLINE ${returnType} ${className}::${commandName}( ${argumentList} ) const VULKAN_HPP_NOEXCEPT { ${functionPointerCheck} ${functionBody}; })"; return replaceWithMap( functionTemplate, { { "argumentList", argumentList }, { "className", initialSkipCount ? stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ) : "Context" }, { "commandName", commandName }, { "functionBody", functionBody }, { "functionPointerCheck", generateFunctionPointerCheck( name, commandData.requiredBy, true ) }, { "nodiscard", nodiscard }, { "returnType", returnType }, { "vkCommandName", name } } ); } else { std::string const declarationTemplate = R"( // wrapper function for command ${vkCommandName}, see https://registry.khronos.org/vulkan/specs/latest/man/html/${vkCommandName}.html ${nodiscard} ${returnType} ${commandName}( ${argumentList} ) const VULKAN_HPP_NOEXCEPT; )"; return replaceWithMap( declarationTemplate, { { "argumentList", argumentList }, { "commandName", commandName }, { "nodiscard", nodiscard }, { "returnType", returnType }, { "vkCommandName", name } } ); } } std::pair VulkanHppGenerator::generateRAIIHandleConstructor( std::pair const & handle, std::map::const_iterator constructorIt, std::string const & enter, std::string const & leave ) const { std::string singularConstructor, arrayConstructor; if ( constructorIt->second.returnType.type == "VkResult" ) { std::tie( singularConstructor, arrayConstructor ) = generateRAIIHandleConstructorResult( handle, constructorIt, enter, leave ); } else if ( constructorIt->second.returnType.type == "void" ) { std::tie( singularConstructor, arrayConstructor ) = generateRAIIHandleConstructorVoid( handle, constructorIt, enter, leave ); } if ( singularConstructor.empty() && arrayConstructor.empty() ) { throw std::runtime_error( "Never encountered a function like <" + constructorIt->first + "> !" ); } return { singularConstructor, arrayConstructor }; } std::pair VulkanHppGenerator::generateRAIIHandleConstructor1Return2Vector( std::pair const & handle, std::map::const_iterator constructorIt, std::string const & enter, std::string const & leave, size_t returnParam, std::map const & vectorParams ) const { if ( returnParam == std::next( vectorParams.begin() )->first ) { if ( vectorParams.begin()->second.lenParam == std::next( vectorParams.begin() )->second.lenParam ) { if ( constructorIt->second.params[vectorParams.begin()->second.lenParam].type.type == "uint32_t" ) { if ( ( constructorIt->second.params[vectorParams.begin()->first].type.type != "void" ) && !isHandleType( constructorIt->second.params[vectorParams.begin()->first].type.type ) ) { std::string singularConstructor; auto lenParamIt = constructorIt->second.params.begin() + vectorParams.begin()->second.lenParam; if ( !checkEquivalentSingularConstructor( handle.second.constructorIts, constructorIt, lenParamIt ) ) { singularConstructor = generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, true, true ); } return { singularConstructor, generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, true, false ) }; } } } } return {}; } std::pair VulkanHppGenerator::generateRAIIHandleConstructors( std::pair const & handle ) const { const auto [enter, leave] = generateProtection( getProtectFromType( handle.first ) ); std::string singularConstructors, arrayConstructors; for ( auto constructorIt : handle.second.constructorIts ) { // there is a non-const parameter with handle type : the to-be-constructed handle // check for additional enter/leave guards for the constructors auto [constructorEnter, constructorLeave] = generateProtection( getProtectFromTitles( constructorIt->second.requiredBy ) ); if ( constructorEnter == enter ) { constructorEnter.clear(); constructorLeave.clear(); } const auto [singularConstructor, arrayConstructor] = generateRAIIHandleConstructor( handle, constructorIt, constructorEnter, constructorLeave ); arrayConstructors += arrayConstructor; singularConstructors += singularConstructor; } singularConstructors += generateRAIIHandleConstructorTakeOwnership( handle ); return { singularConstructors, arrayConstructors }; } std::string VulkanHppGenerator::generateRAIIHandleConstructorArgument( ParamData const & param, bool definition, bool singular, bool takesOwnership ) const { std::string argument; if ( param.type.isConstPointer() ) { assert( param.type.type.starts_with( "Vk" ) ); assert( param.name.starts_with( "p" ) ); std::string argumentName = startLowerCase( stripPrefix( param.name, "p" ) ); std::string argumentType = stripPrefix( param.type.type, "Vk" ); if ( param.optional ) { assert( param.lenExpression.empty() ); argument = "Optional " + argumentName + ( definition ? "" : " = nullptr" ); } else if ( param.lenExpression.empty() ) { argument = argumentType + " const & " + argumentName; } else if ( singular ) { argument = argumentType + " const & " + stripPluralS( argumentName ); } else { argument = "ArrayProxy<" + argumentType + "> const & " + argumentName; } } else if ( specialPointerTypes.contains( param.type.type ) ) { assert( !param.optional ); assert( param.type.isNonConstPointer() ); argument = param.type.type + " & " + param.name; } else if ( ( param.type.isValue() ) && isHandleType( param.type.type ) ) { if ( takesOwnership ) { assert( !param.optional ); argument = param.type.type + " " + param.name; } else { argument = stripPrefix( param.type.type, "Vk" ); if ( param.optional ) { argument = "Optional"; } argument += " const & " + param.name; } } else { assert( !param.optional ); if ( param.arraySizes.empty() ) { argument = param.type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ) + " "; } else { argument = generateStandardArray( param.type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ), param.arraySizes ) + " const & "; } argument += param.name; } return argument; } std::string VulkanHppGenerator::generateRAIIHandleConstructorArguments( std::pair const & handle, std::map::const_iterator constructorIt, bool singular, bool takesOwnership ) const { auto [parentType, parentName] = getParentTypeAndName( handle ); std::string arguments = parentType + " const & " + parentName; if ( takesOwnership ) { arguments += ", " + handle.first + " " + generateRAIIHandleConstructorParamName( handle.first, handle.second.destructorIt ); } if ( constructorIt != m_commands.end() ) { parentType = "Vk" + parentType; const bool skip = skipLeadingGrandParent( handle ); for ( size_t i = skip ? 1 : 0; i < constructorIt->second.params.size(); i++ ) { ParamData const & param = constructorIt->second.params[i]; // filter parent and handle type if ( ( param.type.type != parentType ) && ( param.type.type != handle.first ) ) { // the specialPointerTypes are considered const-pointers! if ( param.type.isNonConstPointer() && !specialPointerTypes.contains( param.type.type ) ) { // this is supposed to be the returned size on an enumeration function! #if !defined( NDEBUG ) if ( ( param.type.type == "size_t" ) || ( param.type.type == "uint32_t" ) ) { auto typeIt = std::ranges::find_if( constructorIt->second.params, [&handle]( ParamData const & pd ) { return pd.type.type == handle.first; } ); assert( typeIt != constructorIt->second.params.end() ); assert( typeIt->lenExpression == param.name ); } else { assert( vectorMemberByStructure( param.type.type ).type.type == handle.first ); } #endif continue; } else if ( std::ranges::any_of( constructorIt->second.params, [¶m]( ParamData const & pd ) { return pd.lenExpression == param.name; } ) ) { // this is the len of an other parameter, which will be mapped to an ArrayProxy assert( param.type.isValue() && ( param.type.type == "uint32_t" ) ); assert( param.arraySizes.empty() && param.lenExpression.empty() && !param.optional ); continue; } arguments += ", " + generateRAIIHandleConstructorArgument( param, false, singular, takesOwnership ); } } } return arguments; } std::string VulkanHppGenerator::generateRAIIHandleConstructorInitializationList( std::pair const & handle, std::map::const_iterator constructorIt, std::map::const_iterator destructorIt, bool takesOwnership ) const { const auto [parentType, parentName] = getParentTypeAndName( handle ); std::string handleName = generateRAIIHandleConstructorParamName( handle.first, destructorIt ); std::string initializationList; if ( destructorIt != m_commands.end() ) { for ( auto destructorParam : destructorIt->second.params ) { if ( destructorParam.type.type == "Vk" + parentType ) { initializationList += "m_" + parentName + "( " + parentName + " ), "; } else if ( destructorParam.type.type == handle.first ) { if ( takesOwnership ) { initializationList += "m_" + startLowerCase( stripPrefix( handle.first, "Vk" ) ) + "( " + handleName + " ), "; } } else if ( destructorParam.type.type == "VkAllocationCallbacks" ) { assert( destructorParam.type.isConstPointer() && destructorParam.arraySizes.empty() && destructorParam.lenExpression.empty() && destructorParam.optional ); initializationList += "m_allocator( static_cast( allocator ) ), "; } else if ( isHandleType( destructorParam.type.type ) ) { assert( destructorParam.type.isValue() && destructorParam.arraySizes.empty() && destructorParam.lenExpression.empty() && !destructorParam.optional ); initializationList += "m_" + destructorParam.name + "( "; auto constructorParamIt = std::ranges::find_if( constructorIt->second.params, [&destructorParam]( ParamData const & pd ) { return pd.type.type == destructorParam.type.type; } ); if ( constructorParamIt != constructorIt->second.params.end() ) { assert( constructorParamIt->type.isValue() && constructorParamIt->arraySizes.empty() && constructorParamIt->lenExpression.empty() && !constructorParamIt->optional ); if ( constructorParamIt->type.type == "Vk" + parentType ) { initializationList += "*"; } initializationList += constructorParamIt->name; } else { #if !defined( NDEBUG ) bool found = false; #endif for ( auto constructorParam : constructorIt->second.params ) { auto structureIt = m_structs.find( constructorParam.type.type ); if ( structureIt != m_structs.end() ) { auto structureMemberIt = findByType( structureIt->second.members, destructorParam.type.type ); if ( structureMemberIt != structureIt->second.members.end() ) { assert( constructorParam.type.isConstPointer() && constructorParam.arraySizes.empty() && constructorParam.lenExpression.empty() && !constructorParam.optional ); initializationList += startLowerCase( stripPrefix( constructorParam.name, "p" ) ) + "." + structureMemberIt->name; #if !defined( NDEBUG ) found = true; #endif break; } } } assert( found ); } initializationList += " ), "; } else { // we can ignore all other parameters here ! } } } else { if ( !handle.second.secondLevelCommands.empty() ) { assert( !handle.second.constructorIts.empty() ); #if !defined( NDEBUG ) auto constructorCommandIt = m_commands.find( handle.second.constructorIts.front()->first ); #endif assert( ( constructorCommandIt != m_commands.end() ) && ( 1 < constructorCommandIt->second.params.size() ) ); assert( std::next( constructorCommandIt->second.params.begin() )->type.type == "Vk" + parentType ); auto commandIt = m_commands.find( *handle.second.secondLevelCommands.begin() ); assert( ( commandIt != m_commands.end() ) && ( 1 < commandIt->second.params.size() ) ); assert( commandIt->second.params.front().type.type == constructorCommandIt->second.params.front().type.type ); assert( std::next( commandIt->second.params.begin() )->type.type == handle.first ); std::string grandParentType = stripPrefix( commandIt->second.params.front().type.type, "Vk" ); initializationList += "m_" + startLowerCase( grandParentType ) + "( " + parentName + ".get" + grandParentType + "() ), "; } if ( takesOwnership ) { initializationList += "m_" + handleName + "( " + handleName + " ), "; } } return initializationList.empty() ? initializationList : initializationList.substr( 0, initializationList.size() - 2 ); } std::string VulkanHppGenerator::generateRAIIHandleConstructorParamName( std::string const & type, std::map::const_iterator destructorIt ) const { if ( destructorIt != m_commands.end() ) { auto destructorParamIt = std::ranges::find_if( destructorIt->second.params, [&type]( ParamData const & destructorParam ) { return destructorParam.type.type == type; } ); if ( destructorParamIt != destructorIt->second.params.end() ) { assert( std::none_of( std::next( destructorParamIt ), destructorIt->second.params.end(), [&type]( ParamData const & destructorParam ) { return destructorParam.type.type == type; } ) ); if ( !destructorParamIt->type.isValue() ) { return startLowerCase( stripPrefix( stripPluralS( destructorParamIt->name ), "p" ) ); } else { return destructorParamIt->name; } } } return startLowerCase( stripPrefix( type, "Vk" ) ); } std::pair VulkanHppGenerator::generateRAIIHandleConstructorResult( std::pair const & handle, std::map::const_iterator constructorIt, std::string const & enter, std::string const & leave ) const { assert( !constructorIt->second.successCodes.empty() ); assert( constructorIt->second.successCodes[0] == "VK_SUCCESS" ); switch ( constructorIt->second.successCodes.size() ) { case 1: if ( !constructorIt->second.errorCodes.empty() ) { std::vector returnParams = determineReturnParams( constructorIt->second.params ); if ( returnParams.size() == 1 ) { assert( isHandleType( constructorIt->second.params[returnParams[0]].type.type ) ); std::map vectorParams = determineVectorParams( constructorIt->second.params ); switch ( vectorParams.size() ) { case 0: return { generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, false, false ), "" }; case 1: if ( returnParams[0] == vectorParams.begin()->first ) { if ( isLenByStructMember( constructorIt->second.params[vectorParams.begin()->first].lenExpression, constructorIt->second.params[vectorParams.begin()->second.lenParam] ) ) { return { "", generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, true, false ) }; } } break; case 2 : return generateRAIIHandleConstructor1Return2Vector( handle, constructorIt, enter, leave, returnParams[0], vectorParams ); default: break; } } } break; case 2: case 3: if ( !constructorIt->second.errorCodes.empty() ) { std::vector returnParams = determineReturnParams( constructorIt->second.params ); switch ( returnParams.size() ) { case 1: { std::map vectorParams = determineVectorParams( constructorIt->second.params ); if ( isHandleType( constructorIt->second.params[returnParams[0]].type.type ) ) { if ( vectorParams.size() == 2 ) { return generateRAIIHandleConstructor1Return2Vector( handle, constructorIt, enter, leave, returnParams[0], vectorParams ); } } else if ( ( vectorParams.size() == 1 ) && ( vectorParams.begin()->first == returnParams[0] ) && vectorParams.begin()->second.byStructure ) { assert( isHandleTypeByStructure( constructorIt->second.params[returnParams[0]].type.type ) ); return { "", generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, true, false ) }; } } break; case 2: if ( ( constructorIt->second.params[returnParams[0]].type.type == "size_t" ) || ( constructorIt->second.params[returnParams[0]].type.type == "uint32_t" ) ) { assert( isHandleType( constructorIt->second.params[returnParams[1]].type.type ) ); std::map vectorParams = determineVectorParams( constructorIt->second.params ); if ( vectorParams.size() == 1 ) { if ( returnParams[0] == vectorParams.begin()->second.lenParam ) { assert( returnParams[1] == vectorParams.begin()->first ); assert( constructorIt->second.successCodes[1] == "VK_INCOMPLETE" ); return { "", generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, true, false ) }; } } } break; default: break; } } break; case 4: if ( !constructorIt->second.errorCodes.empty() ) { std::vector returnParams = determineReturnParams( constructorIt->second.params ); if ( returnParams.size() == 1 ) { assert( isHandleType( constructorIt->second.params[returnParams[0]].type.type ) ); std::map vectorParams = determineVectorParams( constructorIt->second.params ); if ( vectorParams.size() == 2 ) { return generateRAIIHandleConstructor1Return2Vector( handle, constructorIt, enter, leave, returnParams[0], vectorParams ); } } } break; default: break; } return {}; } std::string VulkanHppGenerator::generateRAIIHandleConstructorByCall( std::pair const & handle, std::map::const_iterator constructorIt, std::string const & enter, std::string const & leave, bool isPlural, bool forceSingular ) const { const auto [parentType, parentName] = getParentTypeAndName( handle ); size_t skipCount = 0; if ( constructorIt->second.handle == constructorIt->second.params[skipCount].type.type ) { ++skipCount; } if ( handle.second.parent == constructorIt->second.params[skipCount].type.type ) { ++skipCount; } std::string constructorArguments = generateRAIIHandleConstructorArguments( handle, constructorIt, forceSingular, false ); std::string createCall = generateCommandName( constructorIt->first, constructorIt->second.params, skipCount, forceSingular ? CommandFlavourFlagBits::singular : CommandFlavourFlagBits() ); std::string createArguments; for ( size_t i = skipCount; i < constructorIt->second.params.size() - 1; ++i ) { if ( constructorIt->second.params[i].type.isValue() || specialPointerTypes.contains( constructorIt->second.params[i].type.type ) ) { createArguments += constructorIt->second.params[i].name + ", "; } else if ( constructorIt->second.params[i].type.isConstPointer() ) { assert( constructorIt->second.params[i].lenParams.empty() || isPlural ); if ( !constructorIt->second.params[i].lenParams.empty() ) { assert( constructorIt->second.params[i].lenParams.size() == 1 ); const std::string::size_type start = createArguments.find( constructorIt->second.params[i].lenParams[0].first ); assert( start != std::string::npos ); // remove the size/count argument, and its trailing ", " createArguments.replace( start, constructorIt->second.params[i].lenParams[0].first.length() + 2, "" ); } std::string argument = startLowerCase( stripPrefix( constructorIt->second.params[i].name, "p" ) ); if ( isPlural && forceSingular ) { argument = stripPluralS( argument ); } createArguments += argument + ", "; } else { // this is the count argument (by pointer) for an enumeration function -> skip it } } if ( !createArguments.empty() ) { createArguments = stripPostfix( createArguments, ", " ); } assert( ( constructorIt->second.params.back().type.type == handle.first ) || ( vectorMemberByStructure( constructorIt->second.params.back().type.type ).type.type == handle.first ) ); const std::string constructorTemplate = R"( #if !defined( VULKAN_HPP_NO_EXCEPTIONS ) ${enter} ${handleType}${plural}( ${constructorArguments} ) { *this = ${parentName}.${createCall}( ${createArguments} ); } ${leave}#endif )"; return replaceWithMap( constructorTemplate, { { "constructorArguments", constructorArguments }, { "createArguments", createArguments }, { "createCall", createCall }, { "enter", enter }, { "leave", leave }, { "handleType", stripPrefix( handle.first, "Vk" ) }, { "plural", isPlural && !forceSingular ? "s" : "" }, { "parentName", parentName } } ); } std::string VulkanHppGenerator::generateRAIIHandleConstructorTakeOwnership( std::pair const & handle ) const { std::string handleType = stripPrefix( handle.first, "Vk" ); std::string handleName = startLowerCase( handleType ); const auto [parentType, parentName] = getParentTypeAndName( handle ); std::string constructorArguments = generateRAIIHandleConstructorArguments( handle, handle.second.destructorIt, false, true ); std::string initializationList = generateRAIIHandleConstructorInitializationList( handle, handle.second.destructorIt, handle.second.destructorIt, true ); assert( !handle.second.constructorIts.empty() ); if ( ( 1 < handle.second.constructorIts[0]->second.successCodes.size() && ( handle.second.constructorIts[0]->second.successCodes[1] != "VK_INCOMPLETE" ) ) || ( 2 < handle.second.constructorIts[0]->second.successCodes.size() ) ) { #if !defined( NDEBUG ) for ( size_t i = 1; i < handle.second.constructorIts.size(); ++i ) { assert( 1 < handle.second.constructorIts[i]->second.successCodes.size() ); } #endif constructorArguments += ", Result successCode = Result::eSuccess"; if ( isMultiSuccessCodeConstructor( handle.second.constructorIts ) ) { initializationList += ", m_constructorSuccessCode( successCode )"; } } std::string dispatcherInitializer; if ( ( handle.first != "VkInstance" ) && ( handle.first != "VkDevice" ) ) { dispatcherInitializer = "m_dispatcher( " + parentName + ".getDispatcher() )"; } if ( !initializationList.empty() && !dispatcherInitializer.empty() ) { initializationList += ", "; } std::string dispatcherInit; if ( ( handle.first == "VkDevice" ) || ( handle.first == "VkInstance" ) ) { dispatcherInit = "\n m_dispatcher.reset( new detail::" + handleType + "Dispatcher( " + parentName + ".getDispatcher()->vkGet" + handleType + "ProcAddr, static_cast<" + handle.first + ">( m_" + startLowerCase( handleType ) + " ) ) );"; } const std::string constructorTemplate = R"( ${handleType}( ${constructorArguments} ) : ${initializationList}${dispatcherInitializer} {${dispatcherInit}} )"; return replaceWithMap( constructorTemplate, { { "constructorArguments", constructorArguments }, { "dispatcherInitializer", dispatcherInitializer }, { "dispatcherInit", dispatcherInit }, { "handleType", handleType }, { "initializationList", initializationList } } ); } std::pair VulkanHppGenerator::generateRAIIHandleConstructorVoid( std::pair const & handle, std::map::const_iterator constructorIt, std::string const & enter, std::string const & leave ) const { assert( constructorIt->second.successCodes.empty() && constructorIt->second.errorCodes.empty() ); std::vector returnParams = determineReturnParams( constructorIt->second.params ); if ( returnParams.size() == 1 ) { assert( isHandleType( constructorIt->second.params[returnParams[0]].type.type ) ); std::map vectorParams = determineVectorParams( constructorIt->second.params ); if ( vectorParams.empty() ) { return { generateRAIIHandleConstructorByCall( handle, constructorIt, enter, leave, false, false ), "" }; } } return {}; } std::string VulkanHppGenerator::generateRAIIHandleContext( std::pair const & handle, std::set const & specialFunctions ) const { const std::string contextTemplate = R"( class Context { public: #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL Context() : m_dispatcher( new detail::ContextDispatcher( m_dynamicLoader.getProcAddress( "vkGetInstanceProcAddr" ) ) ) #else Context( PFN_vkGetInstanceProcAddr getInstanceProcAddr ) : m_dispatcher( new detail::ContextDispatcher( getInstanceProcAddr ) ) #endif {} ~Context() = default; Context( Context const & ) = delete; Context( Context && rhs ) VULKAN_HPP_NOEXCEPT #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL : m_dynamicLoader( std::move( rhs.m_dynamicLoader ) ) , m_dispatcher( rhs.m_dispatcher.release() ) #else : m_dispatcher( rhs.m_dispatcher.release() ) #endif {} Context & operator=( Context const & ) = delete; Context & operator=( Context && rhs ) VULKAN_HPP_NOEXCEPT { if ( this != &rhs ) { #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL m_dynamicLoader = std::move( rhs.m_dynamicLoader ); #endif m_dispatcher.reset( rhs.m_dispatcher.release() ); } return *this; } detail::ContextDispatcher const * getDispatcher() const { VULKAN_HPP_ASSERT( m_dispatcher->getVkHeaderVersion() == VK_HEADER_VERSION ); return &*m_dispatcher; } void swap( Context & rhs ) { #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL std::swap( m_dynamicLoader, rhs.m_dynamicLoader ); #endif m_dispatcher.swap( rhs.m_dispatcher ); } ${memberFunctionDeclarations} private: #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL VULKAN_HPP_NAMESPACE::detail::DynamicLoader m_dynamicLoader; #endif std::unique_ptr m_dispatcher; }; )"; return replaceWithMap( contextTemplate, { { "memberFunctionDeclarations", generateRAIIHandleCommandDeclarations( handle, specialFunctions ) } } ); } std::string VulkanHppGenerator::generateRAIIHandleDestructorCallArguments( std::string const & handleType, std::map::const_iterator destructorIt ) const { std::vector arguments; for ( auto param : destructorIt->second.params ) { if ( param.type.type == handleType ) { std::string handleName = startLowerCase( stripPrefix( handleType, "Vk" ) ); if ( param.type.isValue() ) { arguments.push_back( "static_cast<" + handleType + ">( m_" + handleName + " )" ); } else { arguments.push_back( "reinterpret_cast<" + handleType + " const *>( &m_" + handleName + " )" ); } } else if ( param.type.type == "VkAllocationCallbacks" ) { // VULKAN_HPP_NAMESPACE::AllocationCallbacks is stored as a member of the handle class arguments.push_back( "reinterpret_cast( m_allocator )" ); } else if ( isHandleType( param.type.type ) ) { assert( param.arraySizes.empty() ); std::string argument = "m_" + param.name; if ( param.type.isValue() ) { arguments.push_back( "static_cast<" + param.type.type + ">( " + argument + " )" ); } else { assert( param.type.isConstPointer() ); assert( !param.lenExpression.empty() && std::ranges::any_of( destructorIt->second.params, [¶m]( ParamData const & pd ) { return pd.name == param.lenExpression; } ) ); arguments.push_back( "reinterpret_cast<" + param.type.type + " const *>( &" + argument + " )" ); } } else { assert( ( param.type.type == "uint32_t" ) && param.type.isValue() && param.arraySizes.empty() && param.lenExpression.empty() && !param.optional ); assert( std::ranges::any_of( destructorIt->second.params, [¶m]( ParamData const & pd ) { return pd.lenExpression == param.name; } ) ); arguments.push_back( "1" ); } } return generateList( arguments, "", ", " ); } std::tuple VulkanHppGenerator::generateRAIIHandleDetails( std::pair const & handle ) const { std::string getConstructorSuccessCode; const bool multiSuccessCodeContructor = isMultiSuccessCodeConstructor( handle.second.constructorIts ); if ( multiSuccessCodeContructor ) { getConstructorSuccessCode = R"( Result getConstructorSuccessCode() const { return m_constructorSuccessCode; } )"; } const auto [parentType, parentName] = getParentTypeAndName( handle ); std::string handleName = startLowerCase( stripPrefix( handle.first, "Vk" ) ); std::string clearMembers, moveConstructorInitializerList, rawConstructorInitializerList, rawConstructorParams, moveAssignmentInstructions, memberVariables, swapMembers, releaseMembers; if ( handle.second.destructorIt != m_commands.end() ) { clearMembers = " if ( m_" + handleName + " )\n"; clearMembers += " {\n"; clearMembers += " getDispatcher()->" + handle.second.destructorIt->first + "( " + generateRAIIHandleDestructorCallArguments( handle.first, handle.second.destructorIt ) + " );\n"; clearMembers += " }"; for ( auto const & destructorParam : handle.second.destructorIt->second.params ) { std::string memberName, memberType; if ( destructorParam.type.type == "Vk" + parentType ) { memberName = parentName; memberType = "VULKAN_HPP_NAMESPACE::" + parentType; } else if ( destructorParam.type.type == handle.first ) { assert( handle.first.starts_with( "Vk" ) ); memberName = handleName; memberType = "VULKAN_HPP_NAMESPACE::" + stripPrefix( handle.first, "Vk" ); } else if ( std::ranges::none_of( handle.second.destructorIt->second.params, [&destructorParam]( ParamData const & pd ) { return pd.lenExpression == destructorParam.name; } ) ) { std::string name = destructorParam.name; if ( !destructorParam.type.isValue() ) { name = startLowerCase( stripPrefix( name, "p" ) ); } memberName = name; memberType = destructorParam.type.compose( "Vk", m_handles.contains( destructorParam.type.type ) ? "VULKAN_HPP_NAMESPACE" : "" ); } if ( !memberName.empty() ) { clearMembers += "\n m_" + memberName + " = nullptr;"; moveConstructorInitializerList += "m_" + memberName + "( exchange( rhs.m_" + memberName + ", {} ) ), "; rawConstructorInitializerList += "m_" + memberName + "( exchange( " + memberName + ", {} ) ), "; rawConstructorParams += memberType + " " + memberName + ", "; moveAssignmentInstructions += "\n std::swap( m_" + memberName + ", rhs.m_" + memberName + " );"; memberVariables += "\n " + memberType + " m_" + memberName + " = {};"; swapMembers += "\n std::swap( m_" + memberName + ", rhs.m_" + memberName + " );"; if ( destructorParam.type.type != handle.first ) { releaseMembers += "\n m_" + memberName + " = nullptr;"; } } } } else { if ( !handle.second.secondLevelCommands.empty() ) { assert( !handle.second.constructorIts.empty() ); assert( !handle.second.constructorIts.front()->second.params.empty() ); auto const & frontType = handle.second.constructorIts.front()->second.params.front().type.type; assert( isHandleType( frontType ) ); #if !defined( NDEBUG ) auto handleIt = m_handles.find( "Vk" + parentType ); #endif assert( handleIt != m_handles.end() ); assert( handleIt->second.parent == frontType ); std::string frontName = handle.second.constructorIts.front()->second.params.front().name; clearMembers += "\n m_" + frontName + " = nullptr;"; moveConstructorInitializerList = "m_" + frontName + "( exchange( rhs.m_" + frontName + ", {} ) ), "; rawConstructorInitializerList += "m_" + frontName + "( exchange( " + frontName + ", {} ) ), "; rawConstructorParams += "VULKAN_HPP_NAMESPACE::" + stripPrefix( frontType, "Vk" ) + " " + frontName + ", "; moveAssignmentInstructions = "\n std::swap( m_" + frontName + ", rhs.m_" + frontName + " );"; memberVariables = "\n VULKAN_HPP_NAMESPACE::" + stripPrefix( frontType, "Vk" ) + " m_" + frontName + " = {};"; swapMembers = "\n std::swap( m_" + frontName + ", rhs.m_" + frontName + " );"; releaseMembers += "\n m_" + frontName + " = nullptr;"; } clearMembers += "\n m_" + handleName + " = nullptr;"; moveConstructorInitializerList += "m_" + handleName + "( exchange( rhs.m_" + handleName + ", {} ) ), "; rawConstructorInitializerList += "m_" + handleName + "( exchange( " + handleName + ", {} ) ), "; rawConstructorParams += "VULKAN_HPP_NAMESPACE::" + stripPrefix( handle.first, "Vk" ) + " " + handleName + ", "; moveAssignmentInstructions += "\n std::swap( m_" + handleName + ", rhs.m_" + handleName + " );"; assert( handle.first.starts_with( "Vk" ) ); memberVariables += "\n VULKAN_HPP_NAMESPACE::" + stripPrefix( handle.first, "Vk" ) + " m_" + handleName + " = {};"; swapMembers += "\n std::swap( m_" + handleName + ", rhs.m_" + handleName + " );"; } if ( multiSuccessCodeContructor ) { clearMembers += "\n m_constructorSuccessCode = Result::eErrorUnknown;"; memberVariables += "\n Result m_constructorSuccessCode = Result::eErrorUnknown;"; swapMembers += "\n std::swap( m_constructorSuccessCode, rhs.m_constructorSuccessCode );"; moveConstructorInitializerList += "m_constructorSuccessCode( exchange( rhs.m_constructorSuccessCode, {} ) ), "; rawConstructorInitializerList += "m_constructorSuccessCode( exchange( constructorSuccessCode, {} ) ), "; rawConstructorParams += "Result constructorSuccessCode, "; moveAssignmentInstructions += "\n std::swap( m_constructorSuccessCode, rhs.m_constructorSuccessCode );"; releaseMembers += "\n m_constructorSuccessCode = Result::eErrorUnknown;"; } if ( handle.first == "VkInstance" ) { memberVariables += "\n std::unique_ptr m_dispatcher;"; rawConstructorParams += "std::unique_ptr dispatcher"; } else if ( handle.first == "VkDevice" ) { memberVariables += "\n std::unique_ptr m_dispatcher;"; rawConstructorParams += "std::unique_ptr dispatcher"; } else if ( handle.second.constructorIts.front()->second.params.front().type.type == "VkDevice" ) { memberVariables += "\n detail::DeviceDispatcher const * m_dispatcher = nullptr;"; rawConstructorParams += "detail::DeviceDispatcher const * dispatcher"; } else { memberVariables += "\n detail::InstanceDispatcher const * m_dispatcher = nullptr;"; rawConstructorParams += "detail::InstanceDispatcher const * dispatcher"; } clearMembers += "\n m_dispatcher = nullptr;"; swapMembers += "\n std::swap( m_dispatcher, rhs.m_dispatcher );"; releaseMembers += "\n m_dispatcher = nullptr;"; releaseMembers += "\n return exchange( m_" + handleName + ", nullptr );"; if ( ( handle.first == "VkInstance" ) || ( handle.first == "VkDevice" ) ) { moveConstructorInitializerList += "m_dispatcher( rhs.m_dispatcher.release() )"; } else { moveConstructorInitializerList += "m_dispatcher( exchange( rhs.m_dispatcher, nullptr ) )"; } rawConstructorInitializerList += "m_dispatcher( exchange( dispatcher, nullptr ) )"; moveAssignmentInstructions += "\n std::swap( m_dispatcher, rhs.m_dispatcher );"; return std::make_tuple( clearMembers, getConstructorSuccessCode, memberVariables, rawConstructorParams, rawConstructorInitializerList, moveConstructorInitializerList, moveAssignmentInstructions, swapMembers, releaseMembers ); } std::string VulkanHppGenerator::generateRAIIHandleForwardDeclarations( std::vector const & requireData, std::string const & title ) const { std::string str; for ( auto const & require : requireData ) { for ( auto const & type : require.types ) { auto handleIt = findByNameOrAlias( m_handles, type.name ); if ( handleIt != m_handles.end() ) { str += ( handleIt->first == type.name ) ? ( " class " + stripPrefix( handleIt->first, "Vk" ) + ";\n" ) : ( " using " + stripPrefix( type.name, "Vk" ) + " = " + stripPrefix( handleIt->first, "Vk" ) + ";\n" ); } } } return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateRAIIHandles() const { const std::string raiiHandlesTemplate = R"( //======================================== //=== RAII HANDLE forward declarations === //======================================== ${forwardDeclarations} //==================== //=== RAII HANDLES === //==================== template struct isVulkanRAIIHandleType { static VULKAN_HPP_CONST_OR_CONSTEXPR bool value = false; }; ${raiiHandles} // operators to compare VULKAN_HPP_NAMESPACE::raii-handles #if defined(VULKAN_HPP_HAS_SPACESHIP_OPERATOR) template ::value,bool>::type = 0> auto operator<=>( T const & a, T const & b ) VULKAN_HPP_NOEXCEPT { return *a <=> *b; } #else template ::value,bool>::type = 0> bool operator<(T const & a, T const & b ) VULKAN_HPP_NOEXCEPT { return *a < *b; } #endif template ::value,bool>::type = 0> bool operator==( T const & a, T const & b ) VULKAN_HPP_NOEXCEPT { return *a == *b; } template ::value,bool>::type = 0> bool operator!=(T const & a, T const & b ) VULKAN_HPP_NOEXCEPT { return *a != *b; } template ::value,bool>::type = 0> bool operator==( const T & v, std::nullptr_t ) VULKAN_HPP_NOEXCEPT { return !*v; } template ::value,bool>::type = 0> bool operator==( std::nullptr_t, const T & v ) VULKAN_HPP_NOEXCEPT { return !*v; } template ::value,bool>::type = 0> bool operator!=( const T & v, std::nullptr_t ) VULKAN_HPP_NOEXCEPT { return *v; } template ::value,bool>::type = 0> bool operator!=( std::nullptr_t, const T & v ) VULKAN_HPP_NOEXCEPT { return *v; } )"; std::string forwardDeclarations; for ( auto const & feature : m_features ) { forwardDeclarations += generateRAIIHandleForwardDeclarations( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { forwardDeclarations += generateRAIIHandleForwardDeclarations( extension.requireData, extension.name ); } std::set listedHandles; auto handleIt = m_handles.begin(); assert( handleIt->first.empty() ); std::string raiiHandles = generateRAIIHandleContext( *handleIt, m_RAIISpecialFunctions ); for ( ++handleIt; handleIt != m_handles.end(); ++handleIt ) { if ( isTypeUsed( handleIt->first ) ) { raiiHandles += generateRAIIHandle( *handleIt, listedHandles, m_RAIISpecialFunctions ); } } return replaceWithMap( raiiHandlesTemplate, { { "forwardDeclarations", forwardDeclarations }, { "raiiHandles", raiiHandles } } ); } std::string VulkanHppGenerator::generateRAIIHandleSingularConstructorArguments( std::pair const & handle, std::vector const & params, std::string const & argumentType, std::string const & argumentName ) const { std::string arguments = "*reinterpret_cast<" + argumentType + " *>( &" + argumentName + " )"; if ( handle.second.destructorIt != m_commands.end() ) { auto [parentType, parentName] = getParentTypeAndName( handle ); parentType = "Vk" + parentType; for ( auto const & destructorParam : handle.second.destructorIt->second.params ) { if ( ( destructorParam.type.type != parentType ) && ( destructorParam.type.type != handle.first ) && std::ranges::none_of( handle.second.destructorIt->second.params, [&destructorParam]( ParamData const & pd ) { return pd.lenExpression == destructorParam.name; } ) ) { if ( std::ranges::any_of( params, [&destructorParam]( ParamData const & pd ) { return pd.type.type == destructorParam.type.type; } ) ) { if ( isHandleType( destructorParam.type.type ) ) { assert( destructorParam.type.isValue() ); arguments += ", static_cast<" + destructorParam.type.type + ">( *" + destructorParam.name + " )"; } else { assert( destructorParam.type.type == "VkAllocationCallbacks" ); arguments += ", allocator"; } } else { #if !defined( NDEBUG ) bool found = false; #endif for ( auto const & param : params ) { auto structureIt = m_structs.find( param.type.type ); if ( structureIt != m_structs.end() ) { auto memberIt = findByType( structureIt->second.members, destructorParam.type.type ); if ( memberIt != structureIt->second.members.end() ) { #if !defined( NDEBUG ) found = true; #endif assert( !param.type.isValue() ); std::string argument = startLowerCase( stripPrefix( param.name, "p" ) ) + "." + memberIt->name; if ( isHandleType( memberIt->type.type ) ) { argument = "static_cast<" + memberIt->type.type + ">( " + argument + " )"; } arguments += ", " + argument; break; } } } assert( found ); } } } } return arguments; } template std::string VulkanHppGenerator::generateReplacedExtensionsList( Predicate p, Extraction e ) const { std::string extensionsList, previousEnter, previousLeave; for ( auto const & extension : m_extensions ) { if ( p( extension ) ) { const auto [enter, leave] = generateProtection( getProtectFromTitle( extension.name ) ); extensionsList += ( ( previousEnter != enter ) ? ( "\n" + previousLeave + enter ) : "\n" ) + "{ \"" + extension.name + "\", \"" + e( extension ) + "\"}, "; previousEnter = enter; previousLeave = leave; } } if ( !extensionsList.empty() ) { assert( extensionsList.ends_with( ", " ) ); extensionsList = extensionsList.substr( 0, extensionsList.length() - 2 ); if ( !previousLeave.empty() ) { extensionsList += "\n" + previousLeave; } } return extensionsList; } std::string VulkanHppGenerator::generateResultAssignment( CommandData const & commandData ) const { std::string resultAssignment; if ( ( commandData.returnType.type != "void" ) && !( ( commandData.returnType.type == "VkResult" ) && ( commandData.successCodes.size() == 1 ) && commandData.errorCodes.empty() ) ) { resultAssignment = commandData.returnType.type + " result = "; } return resultAssignment; } std::string VulkanHppGenerator::generateResultCheck( CommandData const & commandData, std::string const & className, std::string const & classSeparator, std::string commandName, bool enumerating, bool isMultiPipelineCreation, bool raii ) const { std::string resultCheck; if ( !commandData.errorCodes.empty() ) { std::string successCodeList = generateSuccessCodeList( commandData.successCodes, enumerating ); if ( std::ranges::find( commandData.errorCodes, "VK_ERROR_OUT_OF_DATE_KHR" ) != commandData.errorCodes.end() ) { std::vector extendedSuccessCodes = commandData.successCodes; extendedSuccessCodes.push_back( "VK_ERROR_OUT_OF_DATE_KHR" ); std::string const resultCheckTemplate = R"( #if defined( VULKAN_HPP_HANDLE_ERROR_OUT_OF_DATE_AS_SUCCESS ) ${namespace}detail::resultCheck( result, ${namespaceString} "::${className}${classSeparator}${commandName}"${extendedSuccessCodeList} ); #else ${namespace}detail::resultCheck( result, ${namespaceString} "::${className}${classSeparator}${commandName}"${successCodeList} ); #endif )"; resultCheck = replaceWithMap( resultCheckTemplate, { { "className", className }, { "classSeparator", classSeparator }, { "commandName", commandName }, { "extendedSuccessCodeList", generateSuccessCodeList( extendedSuccessCodes, enumerating ) }, { "namespace", raii ? "VULKAN_HPP_NAMESPACE::" : "" }, { "namespaceString", raii ? "VULKAN_HPP_RAII_NAMESPACE_STRING" : "VULKAN_HPP_NAMESPACE_STRING" }, { "successCodeList", successCodeList } } ); } else if ( isMultiPipelineCreation ) { std::string const resultCheckTemplate = R"(${namespace}detail::resultCheck( result, ${namespaceString} "::${className}${classSeparator}${commandName}"${successCodeList}, ${device}, pipelines, allocator.get(), d );)"; resultCheck = replaceWithMap( resultCheckTemplate, { { "className", className }, { "classSeparator", classSeparator }, { "device", raii ? "static_cast( m_device )" : "m_device" }, { "commandName", commandName }, { "namespace", raii ? "VULKAN_HPP_NAMESPACE::" : "" }, { "namespaceString", raii ? "VULKAN_HPP_RAII_NAMESPACE_STRING" : "VULKAN_HPP_NAMESPACE_STRING" }, { "successCodeList", successCodeList } } ); } else { std::string const resultCheckTemplate = R"(${namespace}detail::resultCheck( result, ${namespaceString} "::${className}${classSeparator}${commandName}"${successCodeList} );)"; resultCheck = replaceWithMap( resultCheckTemplate, { { "className", className }, { "classSeparator", classSeparator }, { "commandName", commandName }, { "namespace", raii ? "VULKAN_HPP_NAMESPACE::" : "" }, { "namespaceString", raii ? "VULKAN_HPP_RAII_NAMESPACE_STRING" : "VULKAN_HPP_NAMESPACE_STRING" }, { "successCodeList", successCodeList } } ); } } return resultCheck; } // Intended only for `enum class Result`! std::string VulkanHppGenerator::generateResultExceptions() const { const std::string templateString = R"( ${enter} class ${className} : public SystemError { public: ${className}( std::string const & message ) : SystemError( make_error_code( ${enumName}::${enumMemberName} ), message ) {} ${className}( char const * message ) : SystemError( make_error_code( ${enumName}::${enumMemberName} ), message ) {} }; ${leave})"; std::string str; auto enumIt = m_enums.find( "VkResult" ); for ( auto const & value : enumIt->second.values ) { if ( value.supported && value.name.starts_with( "VK_ERROR" ) ) { auto [enter, leave] = generateProtection( value.protect ); std::string valueName = generateEnumValueName( enumIt->first, value.name, false ); str += replaceWithMap( templateString, { { "className", stripPrefix( valueName, "eError" ) + "Error" }, { "enter", enter }, { "enumName", stripPrefix( enumIt->first, "Vk" ) }, { "enumMemberName", valueName }, { "leave", leave } } ); } } return str; } std::string VulkanHppGenerator::generateReturnDataType( std::map const & vectorParams, std::vector const & returnParams, bool enumerating, std::vector const & dataTypes, CommandFlavourFlags flavourFlags, bool raii ) const { assert( dataTypes.size() == returnParams.size() ); std::vector modifiedDataTypes( returnParams.size() ); for ( size_t i = 0; i < returnParams.size(); ++i ) { auto vectorParamIt = vectorParams.find( returnParams[i] ); modifiedDataTypes[i] = ( vectorParamIt == vectorParams.end() || ( flavourFlags & CommandFlavourFlagBits::singular ) ) ? dataTypes[i] : ( "std::vector<" + dataTypes[i] + ( raii || ( flavourFlags & CommandFlavourFlagBits::unique ) ? ">" : ( ", " + startUpperCase( stripPrefix( dataTypes[i], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator>" ) ) ); } std::string combinedType; switch ( modifiedDataTypes.size() ) { case 0: combinedType = "void"; break; case 1: combinedType = modifiedDataTypes[0]; break; case 2: assert( !enumerating || ( vectorParams.contains( returnParams[1] ) && ( vectorParams.find( returnParams[1] )->second.lenParam == returnParams[0] ) ) ); combinedType = enumerating ? modifiedDataTypes[1] : ( "std::pair<" + modifiedDataTypes[0] + ", " + modifiedDataTypes[1] + ">" ); break; case 3: switch ( vectorParams.size() ) { case 0: assert( !enumerating ); combinedType = "std::tuple<" + modifiedDataTypes[0] + ", " + modifiedDataTypes[1] + ", " + modifiedDataTypes[2] + ">"; break; case 1: assert( enumerating ); assert( ( vectorParams.begin()->first == returnParams[2] ) && ( vectorParams.begin()->second.lenParam == returnParams[1] ) ); combinedType = "std::pair<" + modifiedDataTypes[0] + ", " + modifiedDataTypes[2] + ">"; break; case 2: assert( enumerating ); assert( ( vectorParams.begin()->first == returnParams[1] ) && ( vectorParams.begin()->second.lenParam == returnParams[0] ) && ( std::next( vectorParams.begin() )->first == returnParams[2] ) && ( std::next( vectorParams.begin() )->second.lenParam == returnParams[0] ) ); combinedType = "std::pair<" + modifiedDataTypes[1] + ", " + modifiedDataTypes[2] + ">"; break; default: assert( false ); break; } break; default: assert( false ); break; } return combinedType; } std::string VulkanHppGenerator::generateReturnStatement( std::string const & commandName, CommandData const & commandData, std::string const & returnVariable, std::string const & returnType, std::string const & decoratedReturnType, std::string const & dataType, size_t initialSkipCount, size_t returnParam, CommandFlavourFlags flavourFlags, bool enumerating, bool raii ) const { const bool unique = flavourFlags & CommandFlavourFlagBits::unique; std::string returnStatement; if ( commandData.returnType.type.starts_with( "Vk" ) ) { if ( ( commandData.successCodes.size() == 1 ) || ( enumerating && ( commandData.successCodes.size() == 2 ) ) ) { assert( commandData.successCodes[0] == "VK_SUCCESS" ); if ( commandData.errorCodes.empty() ) { if ( !returnVariable.empty() ) { returnStatement = "return " + returnVariable + ";"; } } else { returnStatement = "return "s + ( raii ? "VULKAN_HPP_NAMESPACE::" : "" ) + "detail::createResultValueType( "; if ( returnVariable.empty() ) { assert( !unique ); returnStatement += "result );"; } else if ( unique ) { assert( returnParam != INVALID_INDEX ); returnStatement += "result, "; if ( dataType.starts_with( "std::" ) ) { returnStatement += "std::move( unique" + startUpperCase( returnVariable ) + " )"; } else { #if !defined( NDEBUG ) auto typeIt = m_types.find( "Vk" + dataType ); assert( ( typeIt != m_types.end() ) && ( typeIt->second.category == TypeCategory::Handle ) ); #endif returnStatement += returnType + "( " + returnVariable + ", " + generateObjectDeleter( commandName, commandData, initialSkipCount, returnParam ) + " )"; } returnStatement += " );"; } else { returnStatement += "result, std::move( " + returnVariable + " ) );"; } if ( std::ranges::find( commandData.errorCodes, "VK_ERROR_OUT_OF_DATE_KHR" ) != commandData.errorCodes.end() ) { assert( !unique && !returnVariable.empty() ); static std::string returnStatementTemplate = R"( #if defined( VULKAN_HPP_HANDLE_ERROR_OUT_OF_DATE_AS_SUCCESS ) return ResultValue<${returnType}>( result, std::move( ${returnVariable} ) ); #else ${returnStatement} #endif)"; returnStatement = replaceWithMap( returnStatementTemplate, { { "returnStatement", returnStatement }, { "returnType", returnType }, { "returnVariable", returnVariable } } ); } } } else { if ( returnVariable.empty() ) { assert( !unique ); returnStatement = "return static_cast<" + stripPrefix( commandData.returnType.type, "Vk" ) + ">( result );"; } else if ( unique ) { assert( returnParam != INVALID_INDEX ); assert( decoratedReturnType.starts_with( "ResultValue<" ) && decoratedReturnType.ends_with( ">" ) ); returnStatement = "return " + decoratedReturnType + "( result, "; if ( dataType.starts_with( "std::" ) ) { returnStatement += "std::move( unique" + startUpperCase( returnVariable ) + " )"; } else { returnStatement += "UniqueHandle<" + dataType + ", Dispatch>( " + returnVariable + ", " + generateObjectDeleter( commandName, commandData, initialSkipCount, returnParam ) + " )"; } returnStatement += " );"; } else { returnStatement = "return { result, " + returnVariable + " };"; } } } else { assert( !unique ); if ( returnVariable.empty() ) { if ( commandData.returnType.type != "void" ) { returnStatement = "return result;"; } } else { returnStatement = "return " + returnVariable + ";"; } } return returnStatement; } std::string VulkanHppGenerator::generateReturnType( std::vector const & returnParams, std::map const & vectorParams, CommandFlavourFlags flavourFlags, bool raii, std::vector const & dataTypes ) const { assert( returnParams.size() == dataTypes.size() ); const bool chained = flavourFlags & CommandFlavourFlagBits::chained; const bool singular = flavourFlags & CommandFlavourFlagBits::singular; const bool unique = flavourFlags & CommandFlavourFlagBits::unique; assert( !( chained && unique ) ); std::string returnType; switch ( returnParams.size() ) { case 0: returnType = "void"; break; case 1: if ( chained ) { assert( vectorParams.empty() ); returnType = "StructureChain"; } else if ( unique ) { if ( vectorParams.contains( returnParams[0] ) && !singular ) { returnType = "std::vector, " + stripPrefix( dataTypes[0], "VULKAN_HPP_NAMESPACE::" ) + "Allocator>"; } else { returnType = "UniqueHandle<" + dataTypes[0] + ", Dispatch>"; } } else { if ( vectorParams.contains( returnParams[0] ) && !singular ) { returnType = "std::vector<" + dataTypes[0] + ( raii ? "" : ( ", " + stripPrefix( dataTypes[0], "VULKAN_HPP_NAMESPACE::" ) + "Allocator" ) ) + ">"; } else if ( dataTypes[0].starts_with( "Vk" ) ) { returnType = "VULKAN_HPP_NAMESPACE::" + stripPrefix( dataTypes[0], "Vk" ); } else { returnType = dataTypes[0]; } } break; case 2: assert( !unique ); if ( chained ) { #if !defined( NDEBUG ) auto vectorIt = vectorParams.find( returnParams[1] ); #endif assert( ( vectorIt != vectorParams.end() ) && ( vectorIt->second.lenParam == returnParams[0] ) ); returnType = std::string( "std::vector"; } else { if ( vectorParams.contains( returnParams[0] ) ) { if ( singular ) { returnType = "std::pair<" + dataTypes[0] + ", " + dataTypes[1] + ">"; } else { returnType = "std::pair, " + dataTypes[1] + ">"; } } else if ( vectorParams.contains( returnParams[1] ) ) { assert( vectorParams.find( returnParams[1] )->second.lenParam == returnParams[0] ); if ( dataTypes[1].starts_with( "Vk" ) ) { std::string type = stripPrefix( dataTypes[1], "Vk" ); returnType = "std::vector"; } else { returnType = "std::vector<" + dataTypes[1] + ( raii ? "" : ( ", " + startUpperCase( stripPrefix( dataTypes[1], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator" ) ) + ">"; } } else { returnType = "std::pair<" + dataTypes[0] + ", " + dataTypes[1] + ">"; } } break; case 3: assert( !unique ); if ( chained ) { assert( dataTypes[2] == "uint8_t" ); #if !defined( NDEBUG ) auto vectorIt = vectorParams.find( returnParams[2] ); assert( ( vectorIt != vectorParams.end() ) && ( vectorIt->second.lenParam == returnParams[1] ) ); assert( !vectorParams.contains( returnParams[0] ) ); #endif returnType = "std::pair, std::vector<" + dataTypes[2] + ( raii ? "" : ( ", " + startUpperCase( dataTypes[2] ) + "Allocator" ) ) + ">>"; } else { if ( vectorParams.empty() ) { returnType = "std::tuple<" + dataTypes[0] + ", " + dataTypes[1] + ", " + dataTypes[2] + ">"; } else if ( vectorParams.contains( returnParams[1] ) ) { #if !defined( NDEBUG ) auto vectorIt = vectorParams.find( returnParams[1] ); assert( ( vectorIt != vectorParams.end() ) && ( vectorIt->second.lenParam == returnParams[0] ) ); vectorIt = vectorParams.find( returnParams[2] ); assert( ( vectorIt != vectorParams.end() ) && ( vectorIt->second.lenParam == returnParams[0] ) ); #endif returnType = "std::pair, std::vector<" + dataTypes[2] + ( raii ? "" : ( ", " + stripPrefix( dataTypes[2], "VULKAN_HPP_NAMESPACE::" ) + "Allocator" ) ) + ">>"; } else { #if !defined( NDEBUG ) auto vectorIt = vectorParams.find( returnParams[2] ); assert( ( vectorIt != vectorParams.end() ) && ( vectorIt->second.lenParam == returnParams[1] ) ); assert( !vectorParams.contains( returnParams[0] ) ); #endif returnType = "std::pair<" + dataTypes[0] + ", std::vector<" + dataTypes[2] + ( raii ? "" : ( ", " + startUpperCase( stripPrefix( dataTypes[2], "VULKAN_HPP_NAMESPACE::" ) ) + "Allocator" ) ) + ">>"; } } break; default: assert( false ); } return returnType; } std::string VulkanHppGenerator::generateReturnVariable( CommandData const & commandData, std::vector const & returnParams, std::map const & vectorParams, CommandFlavourFlags flavourFlags ) const { const bool chained = flavourFlags & CommandFlavourFlagBits::chained; const bool singular = flavourFlags & CommandFlavourFlagBits::singular; std::string returnVariable; switch ( returnParams.size() ) { case 0: break; // no return variable case 1: if ( chained ) { returnVariable = "structureChain"; } else { auto vectorParamIt = vectorParams.find( returnParams[0] ); if ( ( vectorParamIt != vectorParams.end() ) && vectorParamIt->second.byStructure ) { returnVariable = startLowerCase( stripPrefix( vectorMemberByStructure( commandData.params[returnParams[0]].type.type ).name, "p" ) ); } else { returnVariable = startLowerCase( stripPrefix( commandData.params[returnParams[0]].name, "p" ) ); } if ( singular ) { returnVariable = stripPluralS( returnVariable ); } } break; case 2: if ( chained ) { returnVariable = "structureChains"; } else { auto vectorParamIt = vectorParams.find( returnParams[1] ); if ( vectorParamIt == vectorParams.end() ) { // the return variable is simply named "data_", and holds the multi-return value stuff returnVariable = "data_"; } else { assert( vectorParamIt->second.lenParam == returnParams[0] ); assert( !singular ); returnVariable = startLowerCase( stripPrefix( commandData.params[returnParams[1]].name, "p" ) ); } } break; case 3: assert( !singular ); assert( vectorParams.empty() || ( ( vectorParams.size() == 1 ) && ( vectorParams.begin()->first == returnParams[2] ) && ( vectorParams.begin()->second.lenParam == returnParams[1] ) ) || ( ( vectorParams.size() == 2 ) && ( vectorParams.begin()->first == returnParams[1] ) && ( vectorParams.begin()->second.lenParam == returnParams[0] ) && ( std::next( vectorParams.begin() )->first == returnParams[2] ) && ( std::next( vectorParams.begin() )->second.lenParam == returnParams[0] ) ) ); returnVariable = "data_"; break; default: assert( false ); break; } return returnVariable; } std::string VulkanHppGenerator::generateSizeCheck( std::vector::const_iterator> const & arrayIts, std::string const & structName, bool mutualExclusiveLens ) const { std::string sizeCheck; if ( 1 < arrayIts.size() ) { static const std::string throwTextTemplate = R"( if ( ${throwCheck} ) { throw LogicError( VULKAN_HPP_NAMESPACE_STRING"::${structName}::${structName}: ${throwCheck}" ); } )"; std::string assertionText, throwText; if ( mutualExclusiveLens ) { // exactly one of the arrays has to be non-empty std::string sum; for ( auto it : arrayIts ) { sum += "!" + startLowerCase( stripPrefix( it->name, "p" ) ) + "_.empty() + "; } sum.resize( sum.size() - 3 ); assertionText += " VULKAN_HPP_ASSERT( ( " + sum + " ) <= 1);\n"; throwText += replaceWithMap( throwTextTemplate, { { "structName", structName }, { "throwCheck", "1 < ( " + sum + " )" } } ); } else { for ( size_t first = 0; first + 1 < arrayIts.size(); ++first ) { assert( arrayIts[first]->name.starts_with( "p" ) ); std::string firstName = startLowerCase( stripPrefix( arrayIts[first]->name, "p" ) ) + "_"; for ( auto second = first + 1; second < arrayIts.size(); ++second ) { assert( arrayIts[second]->name.starts_with( "p" ) ); std::string secondName = startLowerCase( stripPrefix( arrayIts[second]->name, "p" ) ) + "_"; std::string assertionCheck = firstName + ".size() == " + secondName + ".size()"; std::string throwCheck = firstName + ".size() != " + secondName + ".size()"; if ( ( !arrayIts[first]->optional.empty() && arrayIts[first]->optional.front() ) || ( !arrayIts[second]->optional.empty() && arrayIts[second]->optional.front() ) ) { assertionCheck = "( " + assertionCheck + " )"; throwCheck = "( " + throwCheck + " )"; if ( !arrayIts[second]->optional.empty() && arrayIts[second]->optional.front() ) { assertionCheck = secondName + ".empty() || " + assertionCheck; throwCheck = "!" + secondName + ".empty() && " + throwCheck; } if ( !arrayIts[first]->optional.empty() && arrayIts[first]->optional.front() ) { assertionCheck = firstName + ".empty() || " + assertionCheck; throwCheck = "!" + firstName + ".empty() && " + throwCheck; } } assertionText += " VULKAN_HPP_ASSERT( " + assertionCheck + " );\n"; throwText += replaceWithMap( throwTextTemplate, { { "structName", structName }, { "throwCheck", throwCheck } } ); } } } sizeCheck += "\n#ifdef VULKAN_HPP_NO_EXCEPTIONS\n" + assertionText + "#else\n" + throwText + "#endif /*VULKAN_HPP_NO_EXCEPTIONS*/\n" + " "; } return sizeCheck; } std::string VulkanHppGenerator::generateStaticAssertions() const { std::string staticAssertions; std::set listedStructs; for ( auto const & feature : m_features ) { staticAssertions += generateStaticAssertions( feature.requireData, feature.name, listedStructs ); } for ( auto const & extension : m_extensions ) { staticAssertions += generateStaticAssertions( extension.requireData, extension.name, listedStructs ); } return staticAssertions; } std::string VulkanHppGenerator::generateStaticAssertions( std::vector const & requireData, std::string const & title, std::set & listedStructs ) const { std::string str; forEachRequiredHandle( requireData, [&str]( std::pair const & handleData ) { std::string const assertionTemplate = R"( VULKAN_HPP_STATIC_ASSERT( sizeof( VULKAN_HPP_NAMESPACE::${className} ) == sizeof( Vk${className} ), "handle and wrapper have different size!" ); VULKAN_HPP_STATIC_ASSERT( std::is_copy_constructible::value, "${className} is not copy_constructible!" ); VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, "${className} is not nothrow_move_constructible!" ); )"; str += replaceWithMap( assertionTemplate, { { "className", stripPrefix( handleData.first, "Vk" ) } } ); } ); forEachRequiredStruct( requireData, [&str, &listedStructs]( std::pair const & structData ) { if ( listedStructs.insert( structData.first ).second ) { std::string const assertionTemplate = R"( VULKAN_HPP_STATIC_ASSERT( sizeof( VULKAN_HPP_NAMESPACE::${structureType} ) == sizeof( Vk${structureType} ), "struct and wrapper have different size!" ); VULKAN_HPP_STATIC_ASSERT( std::is_standard_layout::value, "struct wrapper is not a standard layout!" ); VULKAN_HPP_STATIC_ASSERT( std::is_nothrow_move_constructible::value, "${structureType} is not nothrow_move_constructible!" ); )"; str += replaceWithMap( assertionTemplate, { { "structureType", stripPrefix( structData.first, "Vk" ) } } ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateStruct( std::pair const & structure, std::set & listedStructs ) const { assert( !listedStructs.contains( structure.first ) ); std::string str; for ( auto const & member : structure.second.members ) { auto typeIt = m_types.find( member.type.type ); assert( typeIt != m_types.end() ); if ( ( typeIt->second.category == TypeCategory::Struct ) || ( typeIt->second.category == TypeCategory::Union ) ) { auto structIt = findByNameOrAlias( m_structs, member.type.type ); assert( structIt != m_structs.end() ); if ( ( structure.first != member.type.type ) && !listedStructs.contains( structIt->first ) ) { str += generateStruct( *structIt, listedStructs ); } } else if ( typeIt->second.category == TypeCategory::FuncPointer ) { auto funcPtrIt = m_funcPointers.find( member.type.type ); assert( funcPtrIt != m_funcPointers.end() ); if ( !listedStructs.contains( member.type.type ) ) { str += generateFuncPointer( *funcPtrIt, listedStructs ); } } } if ( !structure.second.subStruct.empty() ) { auto structureIt = m_structs.find( structure.second.subStruct ); if ( ( structureIt != m_structs.end() ) && !listedStructs.contains( structureIt->first ) ) { str += generateStruct( *structureIt, listedStructs ); } } if ( structure.second.isUnion ) { str += generateUnion( structure ); } else { str += generateStructure( structure ); } listedStructs.insert( structure.first ); return str; } std::string VulkanHppGenerator::generateStructCastAssignments( std::pair const & structData ) const { std::string castAssignments; if ( containsDeprecated( structData.second.members ) ) { for ( auto const & member : structData.second.members ) { if ( member.deprecated.empty() && ( member.type.type != "VkStructureType" ) ) { if ( member.type.type.starts_with( "Vk" ) ) { if ( member.type.isValue() ) { castAssignments += member.name + " = static_cast<" + stripPrefix( member.type.type, "Vk" ) + ">( rhs." + member.name + " );\n"; } else { castAssignments += member.name + " = reinterpret_cast<" + member.type.compose( "Vk" ) + ">( rhs." + member.name + " );\n"; } } else { castAssignments += member.name + " = rhs." + member.name + ";\n"; } } } } else { castAssignments = "*this = *reinterpret_cast<" + stripPrefix( structData.first, "Vk" ) + " const *>( &rhs );\n"; } assert( castAssignments.ends_with( "\n" ) ); castAssignments.pop_back(); return castAssignments; } std::string VulkanHppGenerator::generateStructCompareOperators( std::pair const & structData ) const { static const std::set simpleTypes = { "char", "double", "DWORD", "float", "HANDLE", "HINSTANCE", "HMONITOR", "HWND", "int", "int8_t", "int16_t", "int32_t", "int64_t", "LPCWSTR", "size_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t" }; // two structs are compared by comparing each of the elements std::string compareMembers, spaceshipMembers; std::string intro = ""; bool nonDefaultCompare = false; std::string spaceshipOrdering = containsFloatingPoints( structData.second.members ) ? "std::partial_ordering" : "std::strong_ordering"; for ( size_t i = 0; i < structData.second.members.size(); i++ ) { MemberData const & member = structData.second.members[i]; if ( member.deprecated.empty() ) { auto typeIt = m_types.find( member.type.type ); assert( typeIt != m_types.end() ); if ( ( typeIt->second.category == TypeCategory::ExternalType ) && member.type.postfix.empty() && !simpleTypes.contains( member.type.type ) ) { nonDefaultCompare = true; // this type might support operator==() or operator<=>()... that is, use memcmp compareMembers += intro + "( memcmp( &" + member.name + ", &rhs." + member.name + ", sizeof( " + member.type.type + " ) ) == 0 )"; static const std::string spaceshipMemberTemplate = R"( if ( auto cmp = memcmp( &${name}, &rhs.${name}, sizeof( ${type} ) ); cmp != 0 ) return ( cmp < 0 ) ? ${ordering}::less : ${ordering}::greater; )"; spaceshipMembers += replaceWithMap( spaceshipMemberTemplate, { { "name", member.name }, { "ordering", spaceshipOrdering }, { "type", member.type.type } } ); } else if ( member.type.type == "char" && !member.lenExpressions.empty() ) { // compare null-terminated strings nonDefaultCompare = true; assert( member.lenExpressions.size() < 3 ); if ( member.lenExpressions.size() == 1 ) { assert( member.lenExpressions[0] == "null-terminated" ); if ( member.arraySizes.empty() ) { compareMembers += intro + "( ( " + member.name + " == rhs." + member.name + " ) || ( strcmp( " + member.name + ", rhs." + member.name + " ) == 0 ) )"; static const std::string spaceshipMemberTemplate = R"( if ( ${name} != rhs.${name} ) if ( auto cmp = strcmp( ${name}, rhs.${name} ); cmp != 0 ) return ( cmp < 0 ) ? ${ordering}::less : ${ordering}::greater; )"; spaceshipMembers += replaceWithMap( spaceshipMemberTemplate, { { "name", member.name }, { "ordering", spaceshipOrdering } } ); } else { assert( member.arraySizes.size() == 1 ); compareMembers += intro + "( strcmp( " + member.name + ", rhs." + member.name + " ) == 0 )"; static const std::string spaceshipMemberTemplate = R"( if ( auto cmp = strcmp( ${name}, rhs.${name} ); cmp != 0 ) return ( cmp < 0 ) ? ${ordering}::less : ${ordering}::greater; )"; spaceshipMembers += replaceWithMap( spaceshipMemberTemplate, { { "name", member.name }, { "ordering", spaceshipOrdering } } ); } } else { assert( member.lenExpressions[1] == "null-terminated" ); assert( ( member.type.prefix == "const" ) && ( member.type.postfix == "* const *" ) ); static const std::string compareMemberTemplate = R"(std::equal( ${name}, ${name} + ${count}, rhs.${name}, []( char const * left, char const * right ) { return ( left == right ) || ( strcmp( left, right ) == 0 ); } ))"; compareMembers += intro + replaceWithMap( compareMemberTemplate, { { "count", member.lenExpressions[0] }, { "name", member.name } } ); static const std::string spaceshipMemberTemplate = R"( for ( size_t i = 0; i < ${count}; ++i ) { if ( ${name}[i] != rhs.${name}[i] ) if ( auto cmp = strcmp( ${name}[i], rhs.${name}[i] ); cmp != 0 ) return cmp < 0 ? ${ordering}::less : ${ordering}::greater; } )"; spaceshipMembers += replaceWithMap( spaceshipMemberTemplate, { { "count", member.lenExpressions[0] }, { "name", member.name }, { "ordering", spaceshipOrdering } } ); } } else if ( !member.arraySizes.empty() && !member.lenExpressions.empty() ) { nonDefaultCompare = true; assert( ( member.arraySizes.size() == 1 ) && ( member.lenExpressions.size() == 1 ) ); assert( std::ranges::any_of( structData.second.members, [&member]( MemberData const & m ) { return m.name == member.lenExpressions[0]; } ) ); std::string type = member.type.compose( "Vk" ); static const std::string compareMemberTemplate = R"(( memcmp( ${name}, rhs.${name}, ${count} * sizeof( ${type} ) ) == 0 ))"; compareMembers += intro + replaceWithMap( compareMemberTemplate, { { "count", member.lenExpressions[0] }, { "name", member.name }, { "type", type } } ); static const std::string spaceshipMemberTemplate = R"( for ( size_t i = 0; i < ${count}; ++i ) { if ( auto cmp = ${name}[i] <=> rhs.${name}[i]; cmp != 0 ) return cmp; } )"; spaceshipMembers += replaceWithMap( spaceshipMemberTemplate, { { "count", member.lenExpressions[0] }, { "name", member.name } } ); } else { // for all others, we use the operator== of that type compareMembers += intro + "( " + member.name + " == rhs." + member.name + " )"; spaceshipMembers += " if ( auto cmp = " + member.name + " <=> rhs." + member.name + "; cmp != 0 ) return cmp;\n"; } intro = "\n && "; } } std::string structName = stripPrefix( structData.first, "Vk" ); std::string compareBody, spaceshipOperator, spaceshipOperatorIf, spaceshipOperatorElse, spaceshipOperatorEndif; if ( nonDefaultCompare ) { compareBody = " return " + compareMembers + ";"; if ( !containsFuncPointer( structData.first ) ) { static const std::string spaceshipOperatorTemplate = R"( ${ordering} operator<=>( ${name} const & rhs ) const VULKAN_HPP_NOEXCEPT { ${spaceshipMembers} return ${ordering}::equivalent; })"; spaceshipOperatorIf = "#if defined(VULKAN_HPP_HAS_SPACESHIP_OPERATOR)"; spaceshipOperator = replaceWithMap( spaceshipOperatorTemplate, { { "name", structName }, { "ordering", spaceshipOrdering }, { "spaceshipMembers", spaceshipMembers } } ); spaceshipOperatorElse = "#endif\n"; spaceshipOperatorEndif = ""; } } else { // use reflection only if VULKAN_HPP_USE_REFLECT is defined static const std::string compareBodyTemplate = R"(#if defined( VULKAN_HPP_USE_REFLECT ) return this->reflect() == rhs.reflect(); #else return ${compareMembers}; #endif)"; compareBody = replaceWithMap( compareBodyTemplate, { { "compareMembers", compareMembers } } ); if ( !containsFuncPointer( structData.first ) ) { spaceshipOperatorIf = "#if defined(VULKAN_HPP_HAS_SPACESHIP_OPERATOR)"; spaceshipOperator = "auto operator<=>( " + structName + " const & ) const = default;"; spaceshipOperatorElse = "#else"; spaceshipOperatorEndif = "#endif\n"; } } static const std::string compareTemplate = R"( ${spaceshipOperatorIf} ${spaceshipOperator} ${spaceshipOperatorElse} bool operator==( ${name} const & rhs ) const VULKAN_HPP_NOEXCEPT { ${compareBody} } bool operator!=( ${name} const & rhs ) const VULKAN_HPP_NOEXCEPT { return !operator==( rhs ); } ${spaceshipOperatorEndif})"; return replaceWithMap( compareTemplate, { { "name", structName }, { "compareBody", compareBody }, { "spaceshipOperator", spaceshipOperator }, { "spaceshipOperatorElse", spaceshipOperatorElse }, { "spaceshipOperatorEndif", spaceshipOperatorEndif }, { "spaceshipOperatorIf", spaceshipOperatorIf } } ); } std::string VulkanHppGenerator::generateStructConstructors( std::pair const & structData ) const { // the constructor with all the elements as arguments, with defaults // and the simple copy constructor from the corresponding vulkan structure static const std::string constructors = R"(${constexpr}${structName}(${arguments}) VULKAN_HPP_NOEXCEPT ${initializers} {${ignores}} ${constexpr}${structName}( ${structName} const & rhs ) VULKAN_HPP_NOEXCEPT = default; ${structName}( Vk${structName} const & rhs ) VULKAN_HPP_NOEXCEPT : ${structName}( *reinterpret_cast<${structName} const *>( &rhs ) ) {} ${enhancedConstructors} )"; std::vector arguments, initializers; std::string ignores; for ( auto const & member : structData.second.members ) { // gather the arguments std::string argument = generateStructConstructorArgument( member, true ); if ( !argument.empty() ) { arguments.push_back( argument ); } // gather the initializers; skip members with exactly one legal value if ( member.value.empty() ) { initializers.push_back( member.name + "{ " + member.name + "_ }" ); } } auto pNextIt = std::ranges::find_if( structData.second.members, []( MemberData const & md ) { return md.name == "pNext"; } ); if ( pNextIt != structData.second.members.end() ) { // add pNext as a last optional argument to the constructor arguments.push_back( pNextIt->type.compose( "Vk" ) + " pNext_ = nullptr" ); } std::string str = replaceWithMap( constructors, { { "arguments", generateList( arguments, "", ", " ) }, { "constexpr", generateConstexprString( structData ) }, { "enhancedConstructors", structData.second.returnedOnly ? "" : generateStructConstructorsEnhanced( structData ) }, { "structName", stripPrefix( structData.first, "Vk" ) }, { "ignores", ignores }, { "initializers", generateList( initializers, ": ", ", " ) }, { "structName", stripPrefix( structData.first, "Vk" ) } } ); return str; } std::string VulkanHppGenerator::generateStructConstructorsEnhanced( std::pair const & structData ) const { // some structs needs some special handling! if ( structData.first == "VkLayerSettingEXT" ) { assert( ( structData.second.members.size() == 5 ) && ( structData.second.members[0].name == "pLayerName" ) && ( structData.second.members[1].name == "pSettingName" ) && ( structData.second.members[2].name == "type" ) && ( structData.second.members[3].name == "valueCount" ) && ( structData.second.members[4].name == "pValues" ) ); static const std::string byTypeTemplate = R"( LayerSettingEXT( char const * pLayerName_, char const * pSettingName_, LayerSettingTypeEXT type_, ArrayProxyNoTemporaries const & values_ ) : pLayerName( pLayerName_ ) , pSettingName( pSettingName_ ) , type( type_ ) , valueCount( static_cast( values_.size() ) ) , pValues( values_.data() ) { VULKAN_HPP_ASSERT( isSameType<${type}>(type) ); })"; static const std::string constructorTemplate = R"( #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) // NOTE: you need to provide the type because Bool32 and uint32_t are indistinguishable! ${byInt32} ${byInt64} ${byUint32} ${byUint64} ${byFloat32} ${byFloat64} ${byString} #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; return replaceWithMap( constructorTemplate, { { "byInt32", replaceWithMap( byTypeTemplate, { { "type", "int32_t" } } ) }, { "byInt64", replaceWithMap( byTypeTemplate, { { "type", "int64_t" } } ) }, { "byUint32", replaceWithMap( byTypeTemplate, { { "type", "uint32_t" } } ) }, { "byUint64", replaceWithMap( byTypeTemplate, { { "type", "uint64_t" } } ) }, { "byFloat32", replaceWithMap( byTypeTemplate, { { "type", "float" } } ) }, { "byFloat64", replaceWithMap( byTypeTemplate, { { "type", "double" } } ) }, { "byString", replaceWithMap( byTypeTemplate, { { "type", "char *" } } ) }, } ); } else if ( std::ranges::any_of( structData.second.members, [this, &members = structData.second.members]( MemberData const & md ) { return hasLen( md, members ); } ) ) { // map from len-members to all the array members using that len std::map::const_iterator, std::vector::const_iterator>> lenIts; for ( auto mit = structData.second.members.begin(); mit != structData.second.members.end(); ++mit ) { if ( hasLen( *mit, structData.second.members ) ) { std::string lenName = ( mit->lenExpressions.front() == "codeSize / 4" ) ? "codeSize" : mit->lenExpressions.front(); auto lenIt = findByName( structData.second.members, lenName ); assert( lenIt != mit ); lenIts[lenIt].push_back( mit ); } } std::vector arguments, initializers; bool arrayListed = false; std::string templateHeader, sizeChecks, copyOps; for ( auto mit = structData.second.members.begin(); mit != structData.second.members.end(); ++mit ) { // gather the initializers if ( mit->name == "pNext" ) // for pNext, we just get the initializer... the argument is added at the end { initializers.push_back( "pNext( pNext_ )" ); } else if ( mit->value.empty() ) // skip constant members { auto litit = lenIts.find( mit ); if ( litit != lenIts.end() ) { // len arguments just have an initalizer, from the array size initializers.push_back( mit->name + "( " + generateLenInitializer( mit, litit, structData.second.mutualExclusiveLens ) + " )" ); sizeChecks += generateSizeCheck( litit->second, stripPrefix( structData.first, "Vk" ), structData.second.mutualExclusiveLens ); } else if ( hasLen( *mit, structData.second.members ) ) { assert( mit->type.isPointer() || !mit->arraySizes.empty() ); std::string argumentName = ( mit->type.isPointer() ? startLowerCase( stripPrefix( mit->name, "p" ) ) : mit->name ) + "_"; std::string argumentType = mit->type.compose( "Vk" ); if ( mit->type.isPointer() ) { argumentType = trimEnd( stripPostfix( argumentType, "*" ) ); } if ( ( mit->type.type == "void" ) && ( argumentType.find( '*' ) == std::string::npos ) ) { // the argument after stripping one pointer is just void assert( templateHeader.empty() ); templateHeader = " template \n"; const size_t pos = argumentType.find( "void" ); assert( pos != std::string::npos ); argumentType.replace( pos, strlen( "void" ), "T" ); } if ( mit->lenExpressions[0] == "null-terminated" ) { assert( ( mit->type.type == "char" ) && ( mit->arraySizes.size() == 1 ) ); arguments.push_back( "std::string const & " + argumentName ); } else if ( mit->arraySizes.empty() ) { arguments.push_back( "ArrayProxyNoTemporaries<" + argumentType + "> const & " + argumentName ); } else { assert( mit->arraySizes.size() == 1 ); arguments.push_back( "VULKAN_HPP_NAMESPACE::ArrayProxy<" + argumentType + "> const & " + argumentName ); } if ( arrayListed ) { arguments.back() += " = {}"; } arrayListed = true; if ( mit->type.isPointer() ) { initializers.push_back( mit->name + "( " + argumentName + ".data() )" ); } else { assert( mit->deprecated.empty() && ( mit->arraySizes.size() == 1 ) ); if ( mit->lenExpressions[0] == "null-terminated" ) { static const std::string strcpyTemplate = R"( VULKAN_HPP_ASSERT( ${memberName}_.size() < ${arraySize} ); #if defined( _WIN32 ) strncpy_s( ${memberName}, ${memberSize}, ${memberName}_.data(), ${memberName}_.size() ); #else strncpy( ${memberName}, ${memberName}_.data(), std::min( ${memberSize}, ${memberName}_.size() ) ); #endif )"; copyOps += replaceWithMap( strcpyTemplate, { { "arraySize", mit->arraySizes[0] }, { "memberName", mit->name }, { "memberSize", mit->arraySizes[0] } } ); } else { static const std::string memcpyTemplate = R"( VULKAN_HPP_ASSERT( ${memberName}_.size() < ${arraySize} ); memcpy( ${memberName}, ${memberName}_.data(), ${lenExpression} * sizeof( ${argumentType} ) );)"; std::string arraySizeExpression = ( mit->lenExpressions[0] == "null-terminated" ) ? ( "std::min( " + mit->name + "_.size(), " + mit->arraySizes[0] + " )" ) : ( mit->lenExpressions[0] + " * sizeof( " + argumentType + " )" ); copyOps += replaceWithMap( memcpyTemplate, { { "arraySize", mit->arraySizes[0] }, { "argumentType", argumentType }, { "lenExpression", mit->lenExpressions[0] }, { "memberName", mit->name } } ); } } } else { assert( mit->deprecated.empty() ); std::string argument = generateStructConstructorArgument( *mit, arrayListed ); if ( !argument.empty() ) { arguments.push_back( argument ); } initializers.push_back( mit->name + "( " + mit->name + "_ )" ); } } } auto pNextIt = std::ranges::find_if( structData.second.members, []( MemberData const & md ) { return md.name == "pNext"; } ); if ( pNextIt != structData.second.members.end() ) { // add pNext as a last optional argument to the constructor arguments.push_back( pNextIt->type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ) + " pNext_ = nullptr" ); } static const std::string constructorTemplate = R"( #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) ${templateHeader} ${structName}( ${arguments} ) ${initializers} { ${sizeChecks} ${copyOps} } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; return replaceWithMap( constructorTemplate, { { "arguments", generateList( arguments, "", ", " ) }, { "copyOps", copyOps }, { "initializers", generateList( initializers, ": ", ", " ) }, { "sizeChecks", sizeChecks }, { "structName", stripPrefix( structData.first, "Vk" ) }, { "templateHeader", templateHeader } } ); } return ""; } std::string VulkanHppGenerator::generateStructConstructorArgument( MemberData const & memberData, bool withDefault ) const { // skip members 'pNext' and members with a specified value, as they are never explicitly set std::string str; if ( ( memberData.name != "pNext" ) && memberData.value.empty() ) { if ( memberData.type.type.starts_with( "PFN_vk" ) ) { str += "PFN_" + stripPrefix( memberData.type.type, "PFN_vk" ) + " "; } else if ( memberData.arraySizes.empty() ) { str += memberData.type.compose( "Vk" ) + " "; } else { str += generateStandardArray( memberData.type.compose( "Vk" ), memberData.arraySizes ) + " const & "; } str += memberData.name + "_"; if ( withDefault ) { str += " = "; auto enumIt = m_enums.find( memberData.type.type ); if ( enumIt != m_enums.end() && memberData.type.postfix.empty() ) { str += generateEnumInitializer( memberData.type, memberData.arraySizes, enumIt->second.values, enumIt->second.isBitmask ); } else if ( memberData.defaultValue.empty() ) { // if there's no default value, it can be initialized with just {} str += "{}"; } else { str += generateTaggedCamelCase( memberData.defaultValue ); } } } return str; } std::string VulkanHppGenerator::generateStructHashStructure( std::pair const & structure, std::set & listedStructs ) const { assert( !listedStructs.contains( structure.first ) ); std::string str; for ( auto const & member : structure.second.members ) { auto structIt = findByNameOrAlias( m_structs, member.type.type ); if ( ( structIt != m_structs.end() ) && ( structure.first != member.type.type ) && !listedStructs.contains( structIt->first ) ) { str += generateStructHashStructure( *structIt, listedStructs ); } } if ( !containsUnion( structure.first ) ) { static const std::string hashTemplate = R"( ${enter}template <> struct hash { std::size_t operator()(VULKAN_HPP_NAMESPACE::${structureType} const & ${structureName}) const VULKAN_HPP_NOEXCEPT { std::size_t seed = 0; ${hashSum} return seed; } }; ${leave})"; auto [enter, leave] = generateProtection( getProtectFromType( structure.first ) ); std::string structureType = stripPrefix( structure.first, "Vk" ); std::string structureName = startLowerCase( structureType ); str += replaceWithMap( hashTemplate, { { "enter", enter }, { "hashSum", generateStructHashSum( structureName, structure.second.members ) }, { "leave", leave }, { "structureName", structureName }, { "structureType", structureType } } ); } listedStructs.insert( structure.first ); return str; } std::string VulkanHppGenerator::generateStructHashStructures() const { const std::string hashesTemplate = R"( #if 14 <= VULKAN_HPP_CPP_VERSION //====================================== //=== HASH structures for structures === //====================================== # if !defined( VULKAN_HPP_HASH_COMBINE ) # define VULKAN_HPP_HASH_COMBINE( seed, value ) \ seed ^= std::hash::type>{}( value ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ) # endif ${hashes} #endif // 14 <= VULKAN_HPP_CPP_VERSION )"; // Note reordering structs or handles by features and extensions is not possible! std::set listedStructs; std::string hashes; for ( auto const & structure : m_structs ) { if ( !listedStructs.contains( structure.first ) && isTypeUsed( structure.first ) ) { hashes += generateStructHashStructure( structure, listedStructs ); } } return replaceWithMap( hashesTemplate, { { "hashes", hashes } } ); } std::string VulkanHppGenerator::generateStructHashSum( std::string const & structName, std::vector const & members ) const { std::string hashSum; for ( auto const & member : members ) { if ( member.deprecated.empty() ) { if ( !member.arraySizes.empty() ) { assert( member.arraySizes.size() < 3 ); hashSum += " for ( size_t i = 0; i < " + member.arraySizes[0] + "; ++i )\n"; hashSum += " {\n"; if ( member.arraySizes.size() == 1 ) { hashSum += " VULKAN_HPP_HASH_COMBINE( seed, " + structName + "." + member.name + "[i] );\n"; } else { hashSum += " for ( size_t j=0; j < " + member.arraySizes[1] + "; ++j )\n"; hashSum += " {\n"; hashSum += " VULKAN_HPP_HASH_COMBINE( seed, " + structName + "." + member.name + "[i][j] );\n"; hashSum += " }\n"; } hashSum += " }\n"; } else if ( member.type.type == "char" && !member.lenExpressions.empty() ) { assert( member.lenExpressions.size() < 3 ); if ( member.lenExpressions.size() == 1 ) { assert( member.lenExpressions[0] == "null-terminated" ); hashSum += " for ( const char* p = " + structName + "." + member.name + "; *p != '\\0'; ++p )\n"; hashSum += " {\n"; hashSum += " VULKAN_HPP_HASH_COMBINE( seed, *p );\n"; hashSum += " }\n"; } else { assert( member.lenExpressions[1] == "null-terminated" ); hashSum += " for ( size_t i = 0; i < " + structName + "." + member.lenExpressions[0] + "; ++i )\n"; hashSum += " {\n"; hashSum += " for ( const char* p = " + structName + "." + member.name + "[i]; *p != '\\0'; ++p )\n"; hashSum += " {\n"; hashSum += " VULKAN_HPP_HASH_COMBINE( seed, *p );\n"; hashSum += " }\n"; hashSum += " }\n"; } } else { hashSum += " VULKAN_HPP_HASH_COMBINE( seed, " + structName + "." + member.name + " );\n"; } } } assert( !hashSum.empty() ); return hashSum.substr( 0, hashSum.size() - 1 ); } std::string VulkanHppGenerator::generateStructs() const { const std::string structsTemplate = R"( //=============== //=== STRUCTS === //=============== ${structs} )"; // Note reordering structs or handles by features and extensions is not possible! std::set listedStructs; std::string structs; for ( auto const & structure : m_structs ) { if ( !listedStructs.contains( structure.first ) && isTypeUsed( structure.first ) ) { structs += generateStruct( structure, listedStructs ); } } return replaceWithMap( structsTemplate, { { "structs", structs } } ); } std::string VulkanHppGenerator::generateStructure( std::pair const & structure ) const { const auto [enter, leave] = generateProtection( getProtectFromType( structure.first ) ); std::string str = "\n" + enter; std::string constructorsAndSetters; static const std::string constructorsTemplate = R"( #if !defined( VULKAN_HPP_NO_CONSTRUCTORS ) && !defined( VULKAN_HPP_NO_STRUCT_CONSTRUCTORS ) ${pushIgnored}${constructors} ${subConstructors} ${deprecatedConstructors} ${structName} & operator=( ${structName} const & rhs ) VULKAN_HPP_NOEXCEPT = default;${popIgnored} #endif /*VULKAN_HPP_NO_CONSTRUCTORS*/ ${structName} & operator=( Vk${structName} const & rhs ) VULKAN_HPP_NOEXCEPT { ${castAssignments} return *this; } )"; std::string pushIgnored, popIgnored; if ( containsDeprecated( structure.second.members ) ) { pushIgnored = R"( #if defined( _MSC_VER ) # pragma warning( push ) # pragma warning( disable : 4996 ) // 'function': was declared deprecated #elif defined( __clang__ ) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif defined( __GNUC__ ) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #else // unknown compiler... just ignore the warnings for yourselves ;) #endif )"; popIgnored = R"( #if defined( _MSC_VER ) # pragma warning( pop ) #elif defined( __clang__ ) # pragma clang diagnostic pop #elif defined( __GNUC__ ) # pragma GCC diagnostic pop #else // unknown compiler... just ignore the warnings for yourselves ;) #endif )"; } constructorsAndSetters = replaceWithMap( constructorsTemplate, { { "castAssignments", generateStructCastAssignments( structure ) }, { "constructors", generateStructConstructors( structure ) }, { "deprecatedConstructors", generateDeprecatedConstructors( structure.first ) }, { "popIgnored", popIgnored }, { "pushIgnored", pushIgnored }, { "structName", stripPrefix( structure.first, "Vk" ) }, { "subConstructors", generateStructSubConstructor( structure ) } } ); if ( !structure.second.returnedOnly ) { // only structs that are not returnedOnly get setters! constructorsAndSetters += "\n#if !defined( VULKAN_HPP_NO_SETTERS ) && !defined( VULKAN_HPP_NO_STRUCT_SETTERS )"; for ( size_t i = 0; i < structure.second.members.size(); i++ ) { constructorsAndSetters += generateStructSetter( stripPrefix( structure.first, "Vk" ), structure.second.members, i ); } constructorsAndSetters += generateDeprecatedStructSetters( structure.first ); constructorsAndSetters += "#endif /*VULKAN_HPP_NO_SETTERS*/\n"; } std::string structureType = stripPrefix( structure.first, "Vk" ); // the member variables std::string members, memberNames, memberTypes, sTypeValue; std::tie( members, memberNames, memberTypes, sTypeValue ) = generateStructMembers( structure ); // reflect is meaningfull for structs only, filter out unions std::string reflect; if ( !structure.second.isUnion ) { // use reflection only if VULKAN_HPP_USE_REFLECT is defined static const std::string reflectTemplate = R"( #if defined( VULKAN_HPP_USE_REFLECT ) std::tuple<${memberTypes}> reflect() const VULKAN_HPP_NOEXCEPT { return std::tie( ${memberNames} ); } #endif )"; reflect = replaceWithMap( reflectTemplate, { { "memberNames", memberNames }, { "memberTypes", memberTypes } } ); } // operator==() and operator!=() // only structs without a union as a member can have a meaningfull == and != operation; we filter them out std::string compareOperators; if ( !containsUnion( structure.first ) ) { compareOperators += generateStructCompareOperators( structure ); } static const std::string structureTemplate = R"( // wrapper struct for struct Vk${structureType}, see https://registry.khronos.org/vulkan/specs/latest/man/html/Vk${structureType}.html struct ${structureType} { using NativeType = Vk${structureType}; ${allowDuplicate} ${typeValue} ${constructorsAndSetters} operator Vk${structureType} const &() const VULKAN_HPP_NOEXCEPT { return *reinterpret_cast( this ); } operator Vk${structureType} &() VULKAN_HPP_NOEXCEPT { return *reinterpret_cast( this ); } operator Vk${structureType} const *() const VULKAN_HPP_NOEXCEPT { return reinterpret_cast( this ); } operator Vk${structureType} *() VULKAN_HPP_NOEXCEPT { return reinterpret_cast( this ); } ${reflect} ${compareOperators} public: ${members} }; #if 20 <= VULKAN_HPP_CPP_VERSION template <> struct CppType { using Type = ${structureType}; }; #endif )"; std::string allowDuplicate, typeValue; if ( !sTypeValue.empty() ) { allowDuplicate = std::string( " static const bool allowDuplicate = " ) + ( structure.second.allowDuplicate ? "true;" : "false;" ); typeValue = " static VULKAN_HPP_CONST_OR_CONSTEXPR StructureType structureType = StructureType::" + sTypeValue + ";\n"; } str += replaceWithMap( structureTemplate, { { "allowDuplicate", allowDuplicate }, { "constructorsAndSetters", constructorsAndSetters }, { "compareOperators", compareOperators }, { "members", members }, { "reflect", reflect }, { "structureType", structureType }, { "typeValue", typeValue } } ); if ( !sTypeValue.empty() ) { std::string cppTypeTemplate = R"( template <> struct CppType { using Type = ${structureType}; }; )"; str += replaceWithMap( cppTypeTemplate, { { "sTypeValue", sTypeValue }, { "structureType", structureType } } ); } for ( auto const & alias : structure.second.aliases ) { str += " using " + stripPrefix( alias.first, "Vk" ) + " = " + structureType + ";\n"; } str += leave; return str; } std::string VulkanHppGenerator::generateStructExtendsStructs() const { const std::string structExtendsTemplate = R"( //======================= //=== STRUCTS EXTENDS === //======================= ${structExtends})"; std::string structExtends; std::set listedStructs; for ( auto const & feature : m_features ) { structExtends += generateStructExtendsStructs( feature.requireData, listedStructs, feature.name ); } for ( auto const & extension : m_extensions ) { structExtends += generateStructExtendsStructs( extension.requireData, listedStructs, extension.name ); } return replaceWithMap( structExtendsTemplate, { { "structExtends", structExtends } } ); } std::string VulkanHppGenerator::generateStructExtendsStructs( std::vector const & requireData, std::set & listedStructs, std::string const & title ) const { std::string str; forEachRequiredStruct( requireData, [&str, &listedStructs, &title, this]( std::pair const & structData ) { if ( listedStructs.insert( structData.first ).second ) { const auto [enter, leave] = generateProtection( getProtectFromTitle( title ) ); // append all allowed structure chains for ( auto extendName : structData.second.structExtends ) { auto extendsIt = findByNameOrAlias( m_structs, extendName ); const auto [subEnter, subLeave] = generateProtection( getProtectFromType( extendsIt->first ) ); if ( enter != subEnter ) { str += subEnter; } str += " template <> struct StructExtends<" + stripPrefix( structData.first, "Vk" ) + ", " + stripPrefix( extendName, "Vk" ) + ">{ enum { value = true }; };\n"; if ( leave != subLeave ) { str += subLeave; } } } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateStructForwardDeclarations() const { const std::string fowardDeclarationsTemplate = R"( //=================================== //=== STRUCT forward declarations === //=================================== ${forwardDeclarations} )"; std::string forwardDeclarations; std::set listedStructs; for ( auto const & feature : m_features ) { forwardDeclarations += generateStructForwardDeclarations( feature.requireData, feature.name, listedStructs ); } for ( auto const & extension : m_extensions ) { forwardDeclarations += generateStructForwardDeclarations( extension.requireData, extension.name, listedStructs ); } return replaceWithMap( fowardDeclarationsTemplate, { { "forwardDeclarations", forwardDeclarations } } ); } std::string VulkanHppGenerator::generateStructForwardDeclarations( std::vector const & requireData, std::string const & title, std::set & listedStructs ) const { std::string str; forEachRequiredStruct( requireData, [&str, &listedStructs]( std::pair const & structData ) { if ( listedStructs.insert( structData.first ).second ) { std::string structureType = stripPrefix( structData.first, "Vk" ); str += ( structData.second.isUnion ? " union " : " struct " ) + structureType + ";\n"; for ( auto const & alias : structData.second.aliases ) { str += " using " + stripPrefix( alias.first, "Vk" ) + " = " + structureType + ";\n"; } } } ); return addTitleAndProtection( title, str ); } std::tuple VulkanHppGenerator::generateStructMembers( std::pair const & structData ) const { std::string members, memberNames, memberTypes, sTypeValue; for ( auto const & member : structData.second.members ) { members += " "; if ( !member.deprecated.empty() ) { assert( member.deprecated == "ignored" ); members += "VULKAN_HPP_DEPRECATED( \"" + member.deprecated + "\" ) "; } std::string type; if ( !member.bitCount.empty() && member.type.type.starts_with( "Vk" ) ) { assert( member.type.prefix.empty() && member.type.postfix.empty() ); // never encounterd a different case type = member.type.type; } else if ( member.type.type.starts_with( "PFN_vk" ) ) { assert( member.type.prefix.empty() && member.type.postfix.empty() ); // never encounterd a different case type = "PFN_" + stripPrefix( member.type.type, "PFN_vk" ); } else if ( member.arraySizes.empty() ) { type = member.type.compose( "Vk" ); } else { assert( member.type.prefix.empty() && member.type.postfix.empty() ); type = generateStandardArrayWrapper( member.type.compose( "Vk" ), member.arraySizes ); } members += type + " " + member.name; if ( member.deprecated.empty() && !member.value.empty() ) { // special handling for members with legal value: use it as the default members += " = "; if ( member.type.type == "uint32_t" ) { members += member.value; } else { auto enumIt = m_enums.find( member.type.type ); assert( enumIt != m_enums.end() ); assert( containsName( enumIt->second.values, member.value ) ); std::string valueName = generateEnumValueName( enumIt->first, member.value, enumIt->second.isBitmask ); members += stripPrefix( member.type.type, "Vk" ) + "::" + valueName; if ( member.name == "sType" ) { sTypeValue = valueName; } } } else { // when we don't have any default initialization value, everything can be initialized by just '{}' assert( member.arraySizes.empty() || member.bitCount.empty() ); if ( !member.bitCount.empty() ) { assert( member.deprecated.empty() ); members += " : " + member.bitCount; // except for bitfield members, where no default member initializatin // is supported (up to C++20) } else { members += " = "; auto enumIt = m_enums.find( member.type.type ); if ( member.arraySizes.empty() && ( enumIt != m_enums.end() ) && member.type.postfix.empty() ) { members += generateEnumInitializer( member.type, member.arraySizes, enumIt->second.values, enumIt->second.isBitmask ); } else if ( member.defaultValue.empty() ) { members += "{}"; } else { members += generateTaggedCamelCase( member.defaultValue ); } } } members += ";\n"; memberNames += member.name + ", "; memberTypes += type + " const &, "; } return std::make_tuple( members, stripPostfix( memberNames, ", " ), stripPostfix( memberTypes, ", " ), sTypeValue ); } std::string VulkanHppGenerator::generateStructSetter( std::string const & structureName, std::vector const & memberData, size_t index ) const { std::string str; MemberData const & member = memberData[index]; if ( member.type.type != "VkStructureType" ) // filter out StructureType, which is supposed to be immutable ! { static const std::string templateString = R"( ${deprecated}${constexpr}${structureName} & set${MemberName}( ${memberType} ${reference}${memberName}_ ) & VULKAN_HPP_NOEXCEPT { ${assignment}; return *this; } ${deprecated}${constexpr}${structureName} && set${MemberName}( ${memberType} ${reference}${memberName}_ ) && VULKAN_HPP_NOEXCEPT { ${assignment}; return std::move( *this ); } )"; std::string memberType = member.type.type.starts_with( "PFN_vk" ) ? "PFN_" + stripPrefix( member.type.type, "PFN_vk" ) : ( member.arraySizes.empty() ? member.type.compose( "Vk" ) : generateStandardArray( member.type.compose( "Vk" ), member.arraySizes ) ); const bool isReinterpretation = !member.bitCount.empty() && member.type.type.starts_with( "Vk" ); std::string assignment; if ( member.deprecated.empty() ) { if ( isReinterpretation ) { assignment = member.name + " = " + "*reinterpret_cast<" + member.type.type + "*>(&" + member.name + "_)"; } else { assignment = member.name + " = " + member.name + "_"; } } else { assignment = "detail::ignore( " + member.name + "_ )"; } str += replaceWithMap( templateString, { { "assignment", assignment }, { "constexpr", isReinterpretation ? "" : "VULKAN_HPP_CONSTEXPR_14 " }, { "deprecated", member.deprecated.empty() ? "" : "VULKAN_HPP_DEPRECATED( \"" + member.deprecated + "\" ) " }, { "memberName", member.name }, { "MemberName", startUpperCase( member.name ) }, { "memberType", memberType }, { "reference", ( member.type.postfix.empty() && m_structs.contains( member.type.type ) ) ? "const & " : "" }, { "structureName", structureName } } ); if ( hasLen( member, memberData ) ) { assert( member.type.isPointer() || !member.arraySizes.empty() ); std::string arrayName = member.type.isPointer() ? startLowerCase( stripPrefix( member.name, "p" ) ) : member.name; if ( member.lenExpressions[0] == "null-terminated" ) { assert( member.deprecated.empty() && member.lenMembers.empty() && ( member.lenExpressions.size() == 1 ) && ( member.arraySizes.size() == 1 ) && ( member.type.type == "char" ) ); static const std::string setStringTemplate = R"( #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) ${structureName} & set${ArrayName}( std::string const & ${arrayName}_ ) VULKAN_HPP_NOEXCEPT { VULKAN_HPP_ASSERT( ${arrayName}_.size() < ${arraySize} ); #if defined( _WIN32 ) strncpy_s( ${arrayName}, ${arraySize}, ${arrayName}_.data(), ${arrayName}_.size() ); #else strncpy( ${arrayName}, ${arrayName}_.data(), std::min( ${arraySize}, ${arrayName}_.size() ) ); #endif return *this; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; str += replaceWithMap( setStringTemplate, { { "arrayName", arrayName }, { "ArrayName", startUpperCase( arrayName ) }, { "arraySize", member.arraySizes[0] }, { "structureName", structureName } } ); } else if ( ( structureName == "LayerSettingEXT" ) && ( index == 4 ) ) { // VkLayerSettingEXT::pValues needs some special handling! assert( member.deprecated.empty() && ( member.name == "pValues" ) ); static const std::string byTypeTemplate = R"( LayerSettingEXT & setValues( ArrayProxyNoTemporaries const & values_ ) VULKAN_HPP_NOEXCEPT { valueCount = static_cast( values_.size() ); pValues = values_.data(); return *this; })"; static const std::string setArrayTemplate = R"( #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) ${byInt32} ${byInt64} ${byUint32} ${byUint64} ${byFloat32} ${byFloat64} ${byString} #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; return replaceWithMap( setArrayTemplate, { { "byInt32", replaceWithMap( byTypeTemplate, { { "type", "int32_t" } } ) }, { "byInt64", replaceWithMap( byTypeTemplate, { { "type", "int64_t" } } ) }, { "byUint32", replaceWithMap( byTypeTemplate, { { "type", "uint32_t" } } ) }, { "byUint64", replaceWithMap( byTypeTemplate, { { "type", "uint64_t" } } ) }, { "byFloat32", replaceWithMap( byTypeTemplate, { { "type", "float" } } ) }, { "byFloat64", replaceWithMap( byTypeTemplate, { { "type", "double" } } ) }, { "byString", replaceWithMap( byTypeTemplate, { { "type", "char *" } } ) }, } ); } else { assert( ( member.lenExpressions[0] == member.lenMembers[0].first ) || ( member.lenExpressions[0] == "codeSize / 4" ) ); std::string lenName, lenValue; if ( member.lenExpressions[0] == "codeSize / 4" ) { lenName = "codeSize"; lenValue = arrayName + "_.size() * 4"; } else { lenName = member.lenExpressions[0]; lenValue = arrayName + "_.size()"; } if ( member.type.isPointer() ) { assert( memberType.back() == '*' ); memberType = trimEnd( stripPostfix( memberType, "*" ) ); } std::string templateHeader; if ( ( member.type.type == "void" ) && ( memberType.find( '*' ) == std::string::npos ) ) { assert( templateHeader.empty() ); templateHeader = "template \n "; const size_t pos = memberType.find( "void" ); assert( pos != std::string::npos ); memberType.replace( pos, strlen( "void" ), "T" ); lenValue += " * sizeof(T)"; } auto lenMember = findByName( memberData, lenName ); assert( lenMember != memberData.end() && lenMember->type.prefix.empty() && lenMember->type.postfix.empty() ); if ( lenMember->type.type != "size_t" ) { lenValue = "static_cast<" + lenMember->type.type + ">( " + lenValue + " )"; } if ( !member.arraySizes.empty() ) { assert( member.arraySizes.size() == 1 ); lenValue = "(std::min)( " + lenValue + ", " + member.arraySizes[0] + " )"; } if ( member.type.isPointer() ) { std::string functionBody; if ( member.deprecated.empty() ) { static const std::string functionBodyTemplate = R"( ${lenName} = ${lenValue}; ${memberName} = ${arrayName}_.data();)"; functionBody = replaceWithMap( functionBodyTemplate, { { "arrayName", arrayName }, { "lenName", lenName }, { "lenValue", lenValue }, { "memberName", member.name } } ); } else { functionBody = "detail::ignore( " + arrayName + "_ );"; } static const std::string setArrayTemplate = R"( #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) ${deprecated}${templateHeader}${structureName} & set${ArrayName}( ArrayProxyNoTemporaries<${memberType}> const & ${arrayName}_ ) VULKAN_HPP_NOEXCEPT { ${functionBody} return *this; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; str += replaceWithMap( setArrayTemplate, { { "arrayName", arrayName }, { "ArrayName", startUpperCase( arrayName ) }, { "deprecated", member.deprecated.empty() ? "" : "VULKAN_HPP_DEPRECATED( \"" + member.deprecated + "\" ) " }, { "functionBody", functionBody }, { "memberType", memberType }, { "structureName", structureName }, { "templateHeader", templateHeader } } ); } else { assert( member.arraySizes.size() == 1 ); static const std::string setArrayTemplate = R"( #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) ${templateHeader}${structureName} & set${ArrayName}( VULKAN_HPP_NAMESPACE::ArrayProxy<${memberType}> const & ${arrayName}_ ) VULKAN_HPP_NOEXCEPT { VULKAN_HPP_ASSERT( ${arrayName}_.size() <= ${arraySize} ); ${lenName} = ${lenValue}; memcpy( ${arrayName}, ${arrayName}_.data(), ${lenName} ); return *this; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ )"; str += replaceWithMap( setArrayTemplate, { { "arrayName", arrayName }, { "ArrayName", startUpperCase( arrayName ) }, { "arraySize", member.arraySizes[0] }, { "lenName", lenName }, { "lenValue", lenValue }, { "memberType", member.type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ) }, { "structureName", structureName }, { "templateHeader", templateHeader } } ); } } } } return str; } std::string VulkanHppGenerator::generateStructSubConstructor( std::pair const & structData ) const { if ( !structData.second.subStruct.empty() ) { auto const & subStruct = m_structs.find( structData.second.subStruct ); assert( subStruct != m_structs.end() ); std::string subStructArgumentName = startLowerCase( stripPrefix( subStruct->first, "Vk" ) ); std::string subCopies; bool firstArgument = true; for ( size_t i = 0; i < subStruct->second.members.size(); i++ ) { assert( structData.second.members[i].arraySizes.empty() ); static const std::string subCopiesTemplate = R"( ${separator} ${structMemberName}( ${subStructArgumentName}.${subStructMemberName} ) )"; subCopies += replaceWithMap( subCopiesTemplate, { { "separator", firstArgument ? ":" : "," }, { "structMemberName", structData.second.members[i].name }, { "subStructMemberName", subStruct->second.members[i].name }, { "subStructArgumentName", subStructArgumentName } } ); firstArgument = false; } std::vector subArguments; for ( size_t i = subStruct->second.members.size(); i < structData.second.members.size(); i++ ) { std::string argument = generateStructConstructorArgument( structData.second.members[i], true ); if ( !argument.empty() ) { subArguments.push_back( argument ); } assert( structData.second.members[i].arraySizes.empty() ); static const std::string subCopiesTemplate = R"( , ${memberName}( ${memberName}_ ) )"; subCopies += replaceWithMap( subCopiesTemplate, { { "memberName", structData.second.members[i].name } } ); } static const std::string subStructConstructorTemplate = R"( explicit ${structName}( ${subStructName} const & ${subStructArgumentName}${subArguments} ) ${subCopies} {} )"; return replaceWithMap( subStructConstructorTemplate, { { "structName", stripPrefix( structData.first, "Vk" ) }, { "subArguments", generateList( subArguments, ", ", ", " ) }, { "subCopies", subCopies }, { "subStructArgumentName", subStructArgumentName }, { "subStructName", stripPrefix( subStruct->first, "Vk" ) } } ); } return ""; } std::string VulkanHppGenerator::generateSuccessCheck( std::vector const & successCodes ) const { assert( !successCodes.empty() ); std::string successCheck = "result == " + generateSuccessCode( successCodes[0] ); if ( 1 < successCodes.size() ) { successCheck = "( " + successCheck + " )"; for ( size_t i = 1; i < successCodes.size(); ++i ) { successCheck += "|| ( result == " + generateSuccessCode( successCodes[i] ) + " )"; } } return successCheck; } std::string VulkanHppGenerator::generateSuccessCode( std::string const & code ) const { // on each success code: prepend 'Result::e' to the tagged camel case version of the code return "Result::e" + generateTaggedCamelCase( code ); } std::string VulkanHppGenerator::generateSuccessCodeList( std::vector const & successCodes, bool enumerating ) const { std::string successCodeList; size_t skipSize = ( enumerating && ( 1 < successCodes.size() ) && ( successCodes[1] == "VK_INCOMPLETE" ) ) ? 2 : 1; if ( skipSize < successCodes.size() ) { successCodeList = ", { " + generateSuccessCode( successCodes[0] ); for ( size_t i = 1; i < successCodes.size(); ++i ) { successCodeList += ", " + generateSuccessCode( successCodes[i] ); } successCodeList += " }"; } return successCodeList; } std::string VulkanHppGenerator::generateThrowResultException() const { auto enumIt = m_enums.find( "VkResult" ); std::string cases; for ( auto const & value : enumIt->second.values ) { if ( value.supported && value.name.starts_with( "VK_ERROR" ) ) { const auto [enter, leave] = generateProtection( value.protect ); std::string valueName = generateEnumValueName( enumIt->first, value.name, false ); cases += enter + " case Result::" + valueName + ": throw " + stripPrefix( valueName, "eError" ) + "Error( message );\n" + leave; } } cases.pop_back(); // remove last newline const std::string throwTemplate = R"( namespace detail { [[noreturn]] VULKAN_HPP_INLINE void throwResultException( Result result, char const * message ) { switch ( result ) { ${cases} default: throw SystemError( make_error_code( result ), message ); } } })"; return replaceWithMap( throwTemplate, { { "cases", cases } } ); } std::string VulkanHppGenerator::generateTypenameCheck( std::vector const & returnParams, std::map const & vectorParams, std::vector const & chainedReturnParams, bool definition, std::vector const & dataTypes, CommandFlavourFlags flavourFlags ) const { std::string typenameCheck, enableIf; if ( !( flavourFlags & CommandFlavourFlagBits::singular ) ) { for ( size_t i = 0; i < returnParams.size(); ++i ) { if ( vectorParams.contains( returnParams[i] ) ) { std::string elementType = ( ( flavourFlags & CommandFlavourFlagBits::chained ) && std::ranges::any_of( chainedReturnParams, [&returnParams, i]( size_t crp ) { return crp == returnParams[i]; } ) ) ? "StructureChain" : dataTypes[i]; std::string extendedElementType = elementType; if ( flavourFlags & CommandFlavourFlagBits::unique ) { extendedElementType = "UniqueHandle<" + elementType + ", Dispatch>"; } elementType = startUpperCase( stripPrefix( elementType, "VULKAN_HPP_NAMESPACE::" ) ); if ( !enableIf.empty() ) { enableIf += " && "; } enableIf += "std::is_same ::value "; } } if ( !enableIf.empty() ) { typenameCheck = ", typename std::enable_if<" + enableIf + ", int>::type" + ( definition ? "" : " = 0" ); } } return typenameCheck; } std::string VulkanHppGenerator::generateUnion( std::pair const & structure ) const { auto [enter, leave] = generateProtection( getProtectFromType( structure.first ) ); std::string unionName = stripPrefix( structure.first, "Vk" ); bool firstMember = true; std::set listedTypes; // create just one constructor per different type ! std::string constructors; for ( auto memberIt = structure.second.members.begin(); memberIt != structure.second.members.end(); ++memberIt ) { if ( listedTypes.insert( memberIt->type ).second ) { // VkBool32 is aliased to uint32_t. Don't create a VkBool32 constructor if the union also contains a // uint32_t constructor. if ( memberIt->type.type == "VkBool32" ) { if ( findByType( structure.second.members, "uint32_t" ) != structure.second.members.end() ) { continue; } } const bool multipleType = std::any_of( std::next( memberIt ), structure.second.members.end(), [memberIt]( MemberData const & member ) noexcept { return member.type == memberIt->type; } ); std::string memberType = ( memberIt->arraySizes.empty() ) ? memberIt->type.compose( "Vk" ) : ( "const " + generateStandardArray( memberIt->type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ), memberIt->arraySizes ) + "&" ); // In a majority of cases this can be constexpr in C++11 as well, however, determining when exactly // that is the case is a lot more involved and probably not worth it. static const std::string constructorTemplate = R"( VULKAN_HPP_CONSTEXPR_14 ${unionName}( ${memberType} ${argumentName}_${defaultAssignment} ) : ${memberName}( ${argumentName}_ ) {})"; constructors += ( firstMember ? "" : "\n" ) + replaceWithMap( constructorTemplate, { { "argumentName", multipleType ? generateName( memberIt->type ) : memberIt->name }, { "defaultAssignment", firstMember ? " = {}" : "" }, { "memberName", memberIt->name }, { "memberType", memberType }, { "unionName", stripPrefix( structure.first, "Vk" ) } } ); firstMember = false; if ( !memberIt->arraySizes.empty() ) { assert( !multipleType ); assert( memberIt->arraySizes.size() == 1 ); const int size = std::stoi( memberIt->arraySizes[0] ); assert( std::to_string( size ) == memberIt->arraySizes[0] ); std::vector arguments, callArguments; for ( int i = 0; i < size; i++ ) { std::string argumentIndex = std::to_string( i ); arguments.push_back( memberIt->type.type + " " + memberIt->name + "_" + argumentIndex ); callArguments.push_back( memberIt->name + "_" + argumentIndex ); } static const std::string constructorBySequenceTemplate = R"( VULKAN_HPP_CONSTEXPR ${unionName}( ${arguments} ) : ${memberName}{ { { ${callArguments} } } } {})"; constructors += "\n" + replaceWithMap( constructorBySequenceTemplate, { { "arguments", generateList( arguments, "", ", " ) }, { "callArguments", generateList( callArguments, "", ", " ) }, { "memberName", memberIt->name }, { "unionName", stripPrefix( structure.first, "Vk" ) } } ); } } } // one setter per union element std::string setters; for ( size_t i = 0; i < structure.second.members.size(); i++ ) { setters += generateStructSetter( stripPrefix( structure.first, "Vk" ), structure.second.members, i ); } // filter out leading and trailing newline setters = setters.substr( 1, setters.length() - 2 ); // the union member variables std::string members; // if there's at least one Vk... type in this union, check for unrestricted unions support const bool needsUnrestrictedUnions = std::ranges::any_of( structure.second.members, []( MemberData const & member ) { return member.type.type.starts_with( "Vk" ); } ); if ( needsUnrestrictedUnions ) { members += "#ifdef VULKAN_HPP_HAS_UNRESTRICTED_UNIONS\n"; } for ( auto const & member : structure.second.members ) { static const std::string memberTemplate = R"( ${memberType} ${memberName}; )"; members += replaceWithMap( memberTemplate, { { "memberName", member.name }, { "memberType", member.arraySizes.empty() ? member.type.compose( "Vk" ) : generateStandardArrayWrapper( member.type.compose( "Vk", "VULKAN_HPP_NAMESPACE" ), member.arraySizes ) } } ); } if ( needsUnrestrictedUnions ) { members += "#else\n"; for ( auto const & member : structure.second.members ) { static const std::string memberTemplate = R"( ${memberType} ${memberName}${arraySizes}; )"; members += replaceWithMap( memberTemplate, { { "arraySizes", generateCArraySizes( member.arraySizes ) }, { "memberName", member.name }, { "memberType", member.type.compose( "" ) } } ); } members += "#endif /*VULKAN_HPP_HAS_UNRESTRICTED_UNIONS*/\n"; } static const std::string unionTemplate = R"( ${enter} union ${unionName} { using NativeType = Vk${unionName}; #if !defined( VULKAN_HPP_NO_CONSTRUCTORS ) && !defined( VULKAN_HPP_NO_UNION_CONSTRUCTORS ) ${constructors} #endif /*VULKAN_HPP_NO_CONSTRUCTORS*/ #if !defined( VULKAN_HPP_NO_SETTERS ) && !defined( VULKAN_HPP_NO_UNION_SETTERS ) ${setters} #endif /*VULKAN_HPP_NO_SETTERS*/ operator Vk${unionName} const &() const { return *reinterpret_cast( this ); } operator Vk${unionName} &() { return *reinterpret_cast( this ); } ${members} }; #if 20 <= VULKAN_HPP_CPP_VERSION template <> struct CppType { using Type = ${unionName}; }; #endif ${leave})"; return replaceWithMap( unionTemplate, { { "constructors", constructors }, { "enter", enter }, { "leave", leave }, { "members", members }, { "setters", setters }, { "unionName", unionName } } ); } std::string VulkanHppGenerator::generateUniqueHandle( std::pair const & handleData ) const { if ( !handleData.second.deleteCommand.empty() ) { std::string type = stripPrefix( handleData.first, "Vk" ); std::string aliasHandle; for ( auto const & alias : handleData.second.aliases ) { static const std::string aliasHandleTemplate = R"( using Unique${aliasType} = UniqueHandle<${type}, VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>;)"; aliasHandle += replaceWithMap( aliasHandleTemplate, { { "aliasType", stripPrefix( alias.first, "Vk" ) }, { "type", type } } ); } static const std::string uniqueHandleTemplate = R"( template class UniqueHandleTraits<${type}, Dispatch> { public: using deleter = detail::${deleterType}${deleterAction}<${deleterParent}${deleterPool}, Dispatch>; }; using Unique${type} = UniqueHandle<${type}, VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>; ${aliasHandle})"; return replaceWithMap( uniqueHandleTemplate, { { "aliasHandle", aliasHandle }, { "deleterAction", ( handleData.second.deleteCommand.substr( 2, 4 ) == "Free" ) ? "Free" : "Destroy" }, { "deleterParent", handleData.second.destructorType.empty() ? "detail::NoParent" : stripPrefix( handleData.second.destructorType, "Vk" ) }, { "deleterPool", handleData.second.deletePool.empty() ? "" : ", " + stripPrefix( handleData.second.deletePool, "Vk" ) }, { "deleterType", handleData.second.deletePool.empty() ? "Object" : "Pool" }, { "type", type } } ); } return ""; } std::string VulkanHppGenerator::generateUniqueHandle( std::vector const & requireData, std::string const & title, std::set & listedHandles ) const { std::string str; forEachRequiredHandle( requireData, [&str, &listedHandles, this]( std::pair const & handleData ) { if ( listedHandles.insert( handleData.first ).second ) { str += generateUniqueHandle( handleData ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateUniqueHandles() const { std::string uniqueHandlesTemplate = R"( #ifndef VULKAN_HPP_NO_SMART_HANDLE //====================== //=== UNIQUE HANDLEs === //====================== ${uniqueHandles} #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ )"; std::string uniqueHandles; std::set listedHandles; for ( auto const & feature : m_features ) { uniqueHandles += generateUniqueHandle( feature.requireData, feature.name, listedHandles ); } for ( auto const & extension : m_extensions ) { uniqueHandles += generateUniqueHandle( extension.requireData, extension.name, listedHandles ); } if ( uniqueHandles.back() == '\n' ) { uniqueHandles.pop_back(); } return replaceWithMap( uniqueHandlesTemplate, { { "uniqueHandles", uniqueHandles } } ); } std::string VulkanHppGenerator::generateSharedHandle( std::pair const & handleData ) const { if ( !handleData.second.deleteCommand.empty() ) { std::string type = stripPrefix( handleData.first, "Vk" ); std::string aliasHandle; for ( auto const & alias : handleData.second.aliases ) { static const std::string aliasHandleTemplate = R"( using Shared${aliasType} = SharedHandle<${type}>;)"; aliasHandle += replaceWithMap( aliasHandleTemplate, { { "aliasType", stripPrefix( alias.first, "Vk" ) }, { "type", type } } ); } static const std::string sharedHandleTemplate = R"( template <> class SharedHandleTraits<${type}> { public: using DestructorType = ${destructor}; using deleter = detail::${deleterType}${deleterAction}Shared<${type}${deleterPool}>; }; using Shared${type} = SharedHandle<${type}>; ${aliasHandle})"; return replaceWithMap( sharedHandleTemplate, { { "aliasHandle", aliasHandle }, { "deleterAction", ( handleData.second.deleteCommand.substr( 2, 4 ) == "Free" ) ? "Free" : "Destroy" }, { "deleterPool", handleData.second.deletePool.empty() ? "" : ", " + stripPrefix( handleData.second.deletePool, "Vk" ) }, { "deleterType", handleData.second.deletePool.empty() ? "Object" : "Pool" }, { "destructor", handleData.second.destructorType.empty() ? "NoDestructor" : stripPrefix( handleData.second.destructorType, "Vk" ) }, { "type", type } } ); } return ""; } std::string VulkanHppGenerator::generateSharedHandleNoDestroy( std::pair const & handleData ) const { if ( handleData.second.deleteCommand.empty() ) { std::string type = stripPrefix( handleData.first, "Vk" ); std::string aliasHandle; for ( auto const & alias : handleData.second.aliases ) { static const std::string aliasHandleTemplate = R"( using Shared${aliasType} = SharedHandle<${type}>;)"; aliasHandle += replaceWithMap( aliasHandleTemplate, { { "aliasType", stripPrefix( alias.first, "Vk" ) }, { "type", type } } ); } static const std::string sharedHandleTemplate = R"( template <> class SharedHandle<${type}> : public SharedHandleBaseNoDestroy<${type}, ${parent}> { friend SharedHandleBase<${type}, ${parent}>; public: SharedHandle() = default; explicit SharedHandle(${type} handle, ${parent} parent) noexcept : SharedHandleBaseNoDestroy<${type}, ${parent}>(handle, std::move(parent)) {} }; using Shared${type} = SharedHandle<${type}>; ${aliasHandle})"; return replaceWithMap( sharedHandleTemplate, { { "aliasHandle", aliasHandle }, { "parent", "Shared" + stripPrefix( handleData.second.parent, "Vk" ) }, { "type", type } } ); } return ""; } std::string VulkanHppGenerator::generateSharedHandle( std::vector const & requireData, std::string const & title, std::set & listedHandles ) const { std::string str; forEachRequiredHandle( requireData, [&str, &listedHandles, this]( std::pair const & handleData ) { if ( listedHandles.insert( handleData.first ).second ) { str += generateSharedHandle( handleData ); } } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateSharedHandleNoDestroy( std::vector const & requireData, std::string const & title ) const { std::string str; forEachRequiredHandle( requireData, [&str, this]( std::pair const & handleData ) { str += generateSharedHandleNoDestroy( handleData ); } ); return addTitleAndProtection( title, str ); } std::string VulkanHppGenerator::generateSharedHandlesNoDestroy() const { std::string sharedHandles; for ( auto const & feature : m_features ) { sharedHandles += generateSharedHandleNoDestroy( feature.requireData, feature.name ); } for ( auto const & extension : m_extensions ) { sharedHandles += generateSharedHandleNoDestroy( extension.requireData, extension.name ); } assert( sharedHandles.back() == '\n' ); sharedHandles.pop_back(); return sharedHandles; } std::string VulkanHppGenerator::generateSharedHandles() const { std::string sharedHandlesTemplate = R"( //====================== //=== SHARED HANDLEs === //====================== ${sharedHandles} )"; std::string sharedHandles; std::set listedHandles; for ( auto const & feature : m_features ) { sharedHandles += generateSharedHandle( feature.requireData, feature.name, listedHandles ); } for ( auto const & extension : m_extensions ) { sharedHandles += generateSharedHandle( extension.requireData, extension.name, listedHandles ); } if ( sharedHandles.back() == '\n' ) { sharedHandles.pop_back(); } return replaceWithMap( sharedHandlesTemplate, { { "sharedHandles", sharedHandles } } ); } std::string VulkanHppGenerator::generateTaggedCamelCase( std::string const & name ) const { assert( name.starts_with( "VK_" ) ); std::string taggedName = stripPrefix( name, "VK_" ); std::string leadingTag = taggedName.substr( 0, taggedName.find_first_of( '_' ) ); if ( m_tags.contains( leadingTag ) ) { taggedName = stripPrefix( taggedName, leadingTag + "_" ); } else { leadingTag = ""; } std::string trailingTag = taggedName.substr( taggedName.find_last_of( '_' ) + 1 ); if ( m_tags.contains( trailingTag ) ) { taggedName = stripPostfix( taggedName, "_" + trailingTag ); } else { trailingTag = ""; } return leadingTag + toCamelCase( taggedName ) + trailingTag; } std::string VulkanHppGenerator::generateVectorSizeCheck( std::string const & name, CommandData const & commandData, size_t initialSkipCount, std::map> const & countToVectorMap, std::set const & skippedParams, bool onlyThrows ) const { std::string const assertTemplate = " VULKAN_HPP_ASSERT( ${zeroSizeCheck}${firstVectorName}.size() == ${secondVectorName}.size() );"; std::string const assertTemplateVoid = " VULKAN_HPP_ASSERT( ${zeroSizeCheck}${firstVectorName}.size() * sizeof( ${firstDataType} ) == ${secondVectorName}.size() * sizeof( ${secondDataType} ) );"; std::string const assertTemplateSingle = " VULKAN_HPP_ASSERT( ${vectorName}.size() == ${sizeValue} );"; std::string const throwTemplate = R"#( if ( ${zeroSizeCheck}${firstVectorName}.size() != ${secondVectorName}.size() ) { throw LogicError( VULKAN_HPP_NAMESPACE_STRING "::${className}::${commandName}: ${firstVectorName}.size() != ${secondVectorName}.size()" ); })#"; std::string const throwTemplateVoid = R"#( if ( ${zeroSizeCheck}${firstVectorName}.size() * sizeof( ${firstDataType} ) != ${secondVectorName}.size() * sizeof( ${secondDataType} ) ) { throw LogicError( VULKAN_HPP_NAMESPACE_STRING "::${className}::${commandName}: ${firstVectorName}.size() * sizeof( ${firstDataType} ) != ${secondVectorName}.size() * sizeof( ${secondDataType} )" ); })#"; std::string const throwTemplateSingle = R"#( if ( ${vectorName}.size() != ${sizeValue} ) { throw LogicError( VULKAN_HPP_NAMESPACE_STRING "::${className}::${commandName}: ${vectorName}.size() != ${sizeValue}" ); })#"; std::string className = stripPrefix( commandData.params[initialSkipCount - 1].type.type, "Vk" ); std::string commandName = generateCommandName( name, commandData.params, initialSkipCount ); std::string assertions, throws; for ( auto const & cvm : countToVectorMap ) { const size_t defaultStartIndex = determineDefaultStartIndex( commandData.params, skippedParams ); std::string firstVectorName = startLowerCase( stripPrefix( commandData.params[cvm.second[0]].name, "p" ) ); if ( cvm.second.size() == 1 ) { std::string sizeValue; if ( isLenByStructMember( commandData.params[cvm.second[0]].lenExpression, commandData.params[cvm.first] ) ) { std::vector lenParts = tokenize( commandData.params[cvm.second[0]].lenExpression, "->" ); assert( lenParts.size() == 2 ); sizeValue = startLowerCase( stripPrefix( lenParts[0], "p" ) ) + "." + lenParts[1]; } else { assert( !commandData.params[cvm.second[0]].lenParams.empty() ); if ( commandData.params[cvm.first].type.type == "VkSampleCountFlagBits" ) { assert( commandData.params[cvm.second[0]].lenExpression == "(" + commandData.params[cvm.second[0]].lenParams[0].first + " + 31) / 32" ); sizeValue = "( static_cast( " + commandData.params[cvm.second[0]].lenParams[0].first + " ) + 31 ) / 32"; } else { sizeValue = commandData.params[cvm.second[0]].lenExpression; } } assertions += replaceWithMap( assertTemplateSingle, { { "sizeValue", sizeValue }, { "vectorName", firstVectorName } } ); throws += replaceWithMap( throwTemplateSingle, { { "className", className }, { "commandName", commandName }, { "sizeValue", sizeValue }, { "vectorName", firstVectorName } } ); } else { for ( size_t i = 1; i < cvm.second.size(); i++ ) { std::string secondVectorName = startLowerCase( stripPrefix( commandData.params[cvm.second[i]].name, "p" ) ); const bool withZeroSizeCheck = commandData.params[cvm.second[i]].optional && ( defaultStartIndex <= cvm.second[i] ); if ( commandData.params[cvm.second[0]].type.type == "void" ) { assert( commandData.params[cvm.second[i]].type.type == "void" ); std::string firstDataType = stripPrefix( commandData.params[cvm.second[0]].name, "p" ) + "Type"; std::string secondDataType = stripPrefix( commandData.params[cvm.second[i]].name, "p" ) + "Type"; assertions += replaceWithMap( assertTemplateVoid, { { "firstDataType", firstDataType }, { "firstVectorName", firstVectorName }, { "secondDataType", secondDataType }, { "secondVectorName", secondVectorName }, { "zeroSizeCheck", withZeroSizeCheck ? ( secondVectorName + ".empty() || " ) : "" } } ); throws += replaceWithMap( throwTemplateVoid, { { "firstDataType", firstDataType }, { "firstVectorName", firstVectorName }, { "className", className }, { "commandName", commandName }, { "secondDataType", secondDataType }, { "secondVectorName", secondVectorName }, { "zeroSizeCheck", withZeroSizeCheck ? ( "!" + secondVectorName + ".empty() && " ) : "" } } ); } else { assertions += replaceWithMap( assertTemplate, { { "firstVectorName", firstVectorName }, { "secondVectorName", secondVectorName }, { "zeroSizeCheck", withZeroSizeCheck ? ( secondVectorName + ".empty() || " ) : "" } } ); throws += replaceWithMap( throwTemplate, { { "firstVectorName", firstVectorName }, { "className", className }, { "commandName", commandName }, { "secondVectorName", secondVectorName }, { "zeroSizeCheck", withZeroSizeCheck ? ( "!" + secondVectorName + ".empty() && " ) : "" } } ); } if ( i + 1 < cvm.second.size() ) { assertions += "\n"; throws += "\n"; } } } } std::string const sizeCheckTemplate = R"(#ifdef VULKAN_HPP_NO_EXCEPTIONS ${assertions} #else ${throws} #endif /*VULKAN_HPP_NO_EXCEPTIONS*/)"; return onlyThrows ? throws : replaceWithMap( sizeCheckTemplate, { { "assertions", assertions }, { "throws", throws } } ); } std::pair VulkanHppGenerator::getParentTypeAndName( std::pair const & handle ) const { std::string parentType, parentName; if ( handle.first == "VkInstance" ) { parentType = "Context"; parentName = "context"; } else { const bool skip = skipLeadingGrandParent( handle ); assert( !handle.second.constructorIts.empty() && ( ( skip ? 1u : 0u ) < handle.second.constructorIts.front()->second.params.size() ) ); auto const & param = handle.second.constructorIts.front()->second.params[skip ? 1 : 0]; assert( isHandleType( param.type.type ) && param.type.isValue() ); parentType = stripPrefix( param.type.type, "Vk" ); parentName = param.name; } return { parentType, parentName }; } std::string VulkanHppGenerator::getPlatform( std::string const & title ) const { if ( !isSupportedFeature( title ) ) { auto extensionIt = findByName( m_extensions, title ); assert( extensionIt != m_extensions.end() ); return extensionIt->platform; } return ""; } std::pair VulkanHppGenerator::getPoolTypeAndName( std::string const & type ) const { auto structIt = m_structs.find( type ); assert( structIt != m_structs.end() ); auto memberIt = std::ranges::find_if( structIt->second.members, []( MemberData const & md ) { return md.name.find( "Pool" ) != std::string::npos; } ); assert( memberIt != structIt->second.members.end() ); assert( std::none_of( std::next( memberIt ), structIt->second.members.end(), []( MemberData const & md ) { return md.name.find( "Pool" ) != std::string::npos; } ) ); return { memberIt->type.type, memberIt->name }; } std::string VulkanHppGenerator::getProtectFromPlatform( std::string const & platform ) const { auto platformIt = m_platforms.find( platform ); return ( platformIt != m_platforms.end() ) ? platformIt->second.protect : ""; } std::string VulkanHppGenerator::getProtectFromTitle( std::string const & title ) const { if ( !isSupportedFeature( title ) ) { auto extensionIt = findByName( m_extensions, title ); return ( extensionIt != m_extensions.end() ) ? getProtectFromPlatform( extensionIt->platform ) : ""; } return ""; } std::string VulkanHppGenerator::getProtectFromTitles( std::set const & titles ) const { for ( auto titleIt = titles.begin(); titleIt != titles.end(); ++titleIt ) { std::string protect = getProtectFromTitle( *titleIt ); if ( !protect.empty() ) { #if !defined( NDEBUG ) for ( titleIt = std::next( titleIt ); titleIt != titles.end(); ++titleIt ) { std::string p = getProtectFromTitle( *titleIt ); assert( p.empty() || ( p == protect ) ); } #endif return protect; } } return ""; } std::string VulkanHppGenerator::getProtectFromType( std::string const & type ) const { auto typeIt = m_types.find( type ); assert( typeIt != m_types.end() ); return getProtectFromTitles( typeIt->second.requiredBy ); } std::string VulkanHppGenerator::getVectorSize( std::vector const & params, std::map const & vectorParams, size_t returnParam, std::string const & returnParamType, std::set const & templatedParams ) const { std::string vectorSize; auto returnVectorIt = vectorParams.find( returnParam ); assert( returnVectorIt != vectorParams.end() ); if ( returnVectorIt->second.byStructure ) { auto structIt = m_structs.find( params[returnParam].type.type ); assert( structIt != m_structs.end() ); vectorSize = startLowerCase( stripPrefix( params[returnParam].name, "p" ) ) + "." + structIt->second.members[returnVectorIt->second.lenParam].name; } else { std::vector lenParts = tokenize( params[returnParam].lenExpression, "->" ); switch ( lenParts.size() ) { case 1: { std::string const & len = lenParts[0]; size_t lenIdx = std::distance( params.begin(), std::ranges::find_if( params, [&len]( ParamData const & pd ) { return pd.name == len; } ) ); assert( lenIdx < params.size() ); // look for the len, not being the len of the return param, but of an other vector param auto lenVectorParamIt = std::ranges::find_if( vectorParams, [&lenIdx, &returnParam]( auto const & vpi ) { return ( vpi.first != returnParam ) && ( vpi.second.lenParam == lenIdx ); } ); if ( lenVectorParamIt == vectorParams.end() ) { vectorSize = lenParts[0]; if ( templatedParams.contains( returnParam ) ) { vectorSize += " / sizeof( " + returnParamType + " )"; } } else { assert( !templatedParams.contains( returnParam ) ); vectorSize = startLowerCase( stripPrefix( params[lenVectorParamIt->first].name, "p" ) ) + ".size()"; } } break; case 2: assert( vectorParams.contains( returnParam ) ); vectorSize = startLowerCase( stripPrefix( lenParts[0], "p" ) ) + "." + lenParts[1]; break; default: assert( false ); break; } } assert( !vectorSize.empty() ); return vectorSize; } void VulkanHppGenerator::handleRemoval( RemoveData const & removeData ) { for ( auto const & command : removeData.commands ) { bool removed = false; auto commandIt = findByNameOrAlias( m_commands, command ); assert( commandIt != m_commands.end() ); for ( auto const & requiredBy : commandIt->second.requiredBy ) { auto featureIt = findByName( m_features, requiredBy ); if ( featureIt != m_features.end() ) { removed |= handleRemovalCommand( command, featureIt->requireData ); } else { auto extensionIt = std::ranges::find_if( m_extensions, [&requiredBy]( ExtensionData const & ed ) { return ed.name == requiredBy; } ); if ( extensionIt != m_extensions.end() ) { removed |= handleRemovalCommand( command, extensionIt->requireData ); } else { assert( isUnsupportedExtension( requiredBy ) ); removed = true; } } } checkForError( removed, commandIt->second.xmlLine, "to be removed command <" + commandIt->first + "> could not be removed" ); } for ( auto const & e : removeData.enums ) { bool removed = false; for ( auto enumIt = m_enums.begin(); !removed && enumIt != m_enums.end(); ++enumIt ) { auto valueIt = findByName( enumIt->second.values, e ); if ( valueIt != enumIt->second.values.end() ) { enumIt->second.values.erase( valueIt ); removed = true; } } checkForError( removed, 0, "to be removed enum value <" + e + "> could not be removed" ); } for ( auto const & type : removeData.types ) { auto typeIt = m_types.find( type ); assert( typeIt != m_types.end() ); bool removed = typeIt->second.requiredBy.empty(); for ( auto const & requiredBy : typeIt->second.requiredBy ) { auto featureIt = findByName( m_features, requiredBy ); if ( featureIt != m_features.end() ) { removed |= handleRemovalType( type, featureIt->requireData ); } else { auto extensionIt = std::ranges::find_if( m_extensions, [&requiredBy]( ExtensionData const & ed ) { return ed.name == requiredBy; } ); if ( extensionIt != m_extensions.end() ) { removed |= handleRemovalType( type, extensionIt->requireData ); } else { assert( isUnsupportedExtension( requiredBy ) ); removed = true; } } } checkForError( removed, typeIt->second.xmlLine, "to be removed type <" + typeIt->first + "> could not be removed" ); } } bool VulkanHppGenerator::handleRemovalCommand( std::string const & command, std::vector & requireData ) { bool removed = false; for ( auto requireDataIt = requireData.begin(); !removed && ( requireDataIt != requireData.end() ); ++requireDataIt ) { auto requireCommandIt = std::ranges::find_if( requireDataIt->commands, [&command]( auto const & c ) { return c.name == command; } ); if ( requireCommandIt != requireDataIt->commands.end() ) { assert( std::none_of( std::next( requireCommandIt ), requireDataIt->commands.end(), [&command]( auto const & requireCommand ) { return requireCommand.name == command; } ) ); requireDataIt->commands.erase( requireCommandIt ); assert( !requireDataIt->commands.empty() || !requireDataIt->types.empty() ); removed = true; #if !defined( NDEBUG ) for ( auto it = std::next( requireDataIt ); it != requireData.end(); ++it ) { assert( std::ranges::none_of( it->commands, [&command]( auto const & requireCommand ) { return requireCommand.name == command; } ) ); } #endif } } return removed; } void VulkanHppGenerator::handleRemovals() { for ( auto const & feature : m_features ) { for ( auto const & removeData : feature.removeData ) { handleRemoval( removeData ); } } } bool VulkanHppGenerator::handleRemovalType( std::string const & type, std::vector & requireData ) { bool removed = false; for ( auto requireDataIt = requireData.begin(); !removed && ( requireDataIt != requireData.end() ); ) { auto requireTypeIt = std::ranges::find_if( requireDataIt->types, [&type]( auto const & requireType ) { return requireType.name == type; } ); if ( requireTypeIt != requireDataIt->types.end() ) { assert( std::none_of( std::next( requireTypeIt ), requireDataIt->types.end(), [&type]( auto const & requireType ) { return requireType.name == type; } ) ); requireDataIt->types.erase( requireTypeIt ); requireDataIt = ( requireDataIt->commands.empty() && requireDataIt->types.empty() ) ? requireData.erase( requireDataIt ) : std::next( requireDataIt ); removed = true; #if !defined( NDEBUG ) for ( auto it = std::next( requireDataIt ); it != requireData.end(); ++it ) { assert( std::ranges::none_of( it->types, [&type]( auto const & requireType ) { return requireType.name == type; } ) ); } #endif } else { requireDataIt = std::next( requireDataIt ); } } return removed; } bool VulkanHppGenerator::hasArrayConstructor( HandleData const & handleData ) const { for ( auto constructorIt : handleData.constructorIts ) { if ( !determineVectorParams( constructorIt->second.params ).empty() ) { return true; } } return false; } bool VulkanHppGenerator::hasLen( MemberData const & memberData, std::vector const & members ) const { return ( !memberData.lenMembers.empty() && ( ( ( memberData.lenExpressions[0] == memberData.lenMembers[0].first ) && members[memberData.lenMembers[0].second].type.isValue() ) || ( memberData.lenExpressions[0] == ( memberData.lenMembers[0].first + " / 4" ) ) ) ) || ( !memberData.lenExpressions.empty() && ( memberData.lenExpressions[0] == "null-terminated" ) && !memberData.arraySizes.empty() ); } bool VulkanHppGenerator::hasParentHandle( std::string const & handle, std::string const & parent ) const { std::string candidate = handle; while ( !candidate.empty() ) { auto const & handleIt = m_handles.find( candidate ); assert( handleIt != m_handles.end() ); if ( handleIt->second.parent == parent ) { return true; } else { candidate = handleIt->second.parent; } } return false; } bool VulkanHppGenerator::hasPointerParams( std::vector const & params ) const { return std::ranges::any_of( params, []( auto const & pd ) { return pd.type.isPointer(); } ); } bool VulkanHppGenerator::isDeviceCommand( CommandData const & commandData ) const { return !commandData.handle.empty() && !commandData.params.empty() && m_handles.contains( commandData.params[0].type.type ) && ( commandData.params[0].type.type != "VkInstance" ) && ( commandData.params[0].type.type != "VkPhysicalDevice" ); } bool VulkanHppGenerator::isEnumerated( std::string const & type ) const { std::string tag = findTag( type ); std::string untagged = stripPostfix( type, tag ); assert( !untagged.ends_with( "s" ) ); if ( untagged.ends_with( "y" ) ) { untagged.pop_back(); untagged += "ie"; } std::string pluralType = stripPrefix( untagged, "Vk" ) + "s" + tag; return std::ranges::any_of( m_commands, [&pluralType]( auto const & command ) { return command.first.starts_with( "vkEnumerate" ) && command.first.ends_with( pluralType ); } ); } bool VulkanHppGenerator::isExtension( std::string const & name ) const { return isSupportedExtension( name ) || isUnsupportedExtension( name ); } bool VulkanHppGenerator::isFeature( std::string const & name ) const { return isSupportedFeature( name ) || isUnsupportedFeature( name ); } bool VulkanHppGenerator::isHandleType( std::string const & type ) const { return type.starts_with( "Vk" ) && ( findByNameOrAlias( m_handles, type ) != m_handles.end() ); } bool VulkanHppGenerator::isHandleTypeByStructure( std::string const & type ) const { auto structIt = m_structs.find( type ); return ( structIt != m_structs.end() ) && describesVector( structIt->second ) && isHandleType( structIt->second.members[3].type.type ); } bool VulkanHppGenerator::isLenByParam( std::string const & name, std::vector const & params ) const { return std::ranges::find_if( params, [&name]( auto const & pd ) { return pd.name == name; } ) != params.end(); } bool VulkanHppGenerator::isLenByStructMember( std::string const & name, std::vector const & params ) const { // check if name specifies a member of a struct std::vector nameParts = tokenize( name, "->" ); if ( nameParts.size() == 2 ) { auto paramIt = std::ranges::find_if( params, [&n = nameParts[0]]( ParamData const & pd ) { return pd.name == n; } ); if ( paramIt != params.end() ) { #if !defined( NDEBUG ) auto structureIt = m_structs.find( paramIt->type.type ); assert( structureIt != m_structs.end() ); assert( isStructMember( nameParts[1], structureIt->second.members ) ); #endif return true; } } return false; } bool VulkanHppGenerator::isLenByStructMember( std::string const & name, ParamData const & param ) const { // check if name specifies a member of a struct std::vector nameParts = tokenize( name, "->" ); if ( nameParts.size() == 1 ) { // older versions of vk.xml used the notation parameter::member nameParts = tokenize( name, "::" ); } if ( ( nameParts.size() == 2 ) && ( nameParts[0] == param.name ) ) { #if !defined( NDEBUG ) auto structureIt = m_structs.find( param.type.type ); assert( structureIt != m_structs.end() ); assert( isStructMember( nameParts[1], structureIt->second.members ) ); #endif return true; } return false; } bool VulkanHppGenerator::isMultiSuccessCodeConstructor( std::vector::const_iterator> const & constructorIts ) const { bool ok = !constructorIts.empty(); if ( ok ) { auto constructorIt = constructorIts.begin(); ok = ( 2 < ( *constructorIt )->second.successCodes.size() ) || ( ( ( *constructorIt )->second.successCodes.size() == 2 ) && ( ( *constructorIt )->second.successCodes[1] != "VK_INCOMPLETE" ) ); #if !defined( NDEBUG ) for ( constructorIt = std::next( constructorIt ); constructorIt != constructorIts.end(); ++constructorIt ) { assert( ok == ( 2 < ( *constructorIt )->second.successCodes.size() ) || ( ( ( *constructorIt )->second.successCodes.size() == 2 ) && ( ( *constructorIt )->second.successCodes[1] != "VK_INCOMPLETE" ) ) ); } #endif } return ok; } bool VulkanHppGenerator::isParam( std::string const & name, std::vector const & params ) const { return findByName( params, name ) != params.end(); } bool VulkanHppGenerator::isSimpleStructure( std::string const & name ) const { auto structIt = m_structs.find( name ); return ( structIt != m_structs.end() ) && !structureHoldsHandle( structIt->second ) && !structureHoldsVector( structIt->second ); } bool VulkanHppGenerator::isStructMember( std::string const & name, std::vector const & memberData ) const { return findByName( memberData, name ) != memberData.end(); } bool VulkanHppGenerator::isStructureChainAnchor( std::string const & type ) const { if ( type.starts_with( "Vk" ) ) { auto it = findByNameOrAlias( m_structs, type ); if ( it != m_structs.end() ) { return !it->second.extendedBy.empty(); } } return false; } bool VulkanHppGenerator::isStructureType( std::string const & type ) const { return type.starts_with( "Vk" ) && ( findByNameOrAlias( m_structs, type ) != m_structs.end() ); } bool VulkanHppGenerator::isSupported( std::set const & requiredBy ) const { for ( auto const & r : requiredBy ) { if ( isSupportedFeature( r ) || isSupportedExtension( r ) ) { return true; } else { assert( isUnsupportedFeature( r ) || isUnsupportedExtension( r ) ); } } return false; } bool VulkanHppGenerator::isSupportedExtension( std::string const & name ) const { return std::ranges::any_of( m_extensions, [&name]( ExtensionData const & ed ) { return ed.name == name; } ); } bool VulkanHppGenerator::isSupportedFeature( std::string const & name ) const { return std::ranges::any_of( m_features, [&name]( FeatureData const & fd ) { return fd.name == name; } ); } bool VulkanHppGenerator::isTypeRequired( std::string const & type ) const { auto typeIt = m_types.find( type ); assert( typeIt != m_types.end() ); return !typeIt->second.requiredBy.empty(); } bool VulkanHppGenerator::isTypeUsed( std::string const & type ) const { auto typeIt = m_types.find( type ); assert( typeIt != m_types.end() ); for ( auto const & require : typeIt->second.requiredBy ) { auto featureIt = findByName( m_features, require ); if ( featureIt != m_features.end() ) { for ( auto const & r : featureIt->requireData ) { if ( std::ranges::any_of( r.types, [&type]( auto const & requireType ) { return requireType.name == type; } ) ) { return true; } } } else { auto extensionIt = findByName( m_extensions, require ); if ( extensionIt != m_extensions.end() ) { for ( auto const & r : extensionIt->requireData ) { if ( std::ranges::any_of( r.types, [&type]( auto const & requireType ) { return requireType.name == type; } ) ) { return true; } } } } } return false; } bool VulkanHppGenerator::isUnsupportedExtension( std::string const & name ) const { return std::ranges::any_of( m_unsupportedExtensions, [&name]( auto const & ed ) { return ed.name == name; } ); } bool VulkanHppGenerator::isUnsupportedFeature( std::string const & name ) const { return std::ranges::any_of( m_unsupportedFeatures, [&name]( auto const & ed ) { return ed.name == name; } ); } bool VulkanHppGenerator::isVectorByStructure( std::string const & type ) const { auto structIt = m_structs.find( type ); return ( structIt != m_structs.end() ) && describesVector( structIt->second ); } void VulkanHppGenerator::markExtendedStructs() { for ( auto const & s : m_structs ) { for ( auto const & extends : s.second.structExtends ) { auto structIt = findByNameOrAlias( m_structs, extends ); checkForError( structIt != m_structs.end(), s.second.xmlLine, "struct <" + s.first + "> extends unknown struct <" + extends + ">" ); checkForError( structIt->second.extendedBy.insert( s.first ).second, structIt->second.xmlLine, "struct <" + structIt->first + "> already extended by <" + extends + ">" ); } } } void VulkanHppGenerator::mergeInternalFeatures() { // separate the internal and public features std::map internalFeatures; for ( auto it = m_features.begin(); it != m_features.end(); ) { if ( it->isInternal ) { internalFeatures.insert( { it->name, std::move( *it ) } ); it = m_features.erase( it ); } else { ++it; } } for ( auto & feature : m_features ) { // Check if the public feature supports the current API bool publicFeatureSupported = std::ranges::any_of( feature.api, [this]( auto const & a ) { return a == m_api; } ); if ( publicFeatureSupported ) { // Recursively merge all internal features that the public feature depends on mergeInternalFeatures( feature.depends, feature, internalFeatures ); } } // Check that all internal features have been processed checkForError( internalFeatures.empty(), 0, "not all internal features were merged into public features" ); } void VulkanHppGenerator::mergeInternalFeatures( std::vector const & depends, FeatureData & feature, std::map & internalFeatures ) { for ( auto const & dep : depends ) { auto depIt = internalFeatures.find( dep ); if ( depIt != internalFeatures.end() ) { // Recursively merge dependencies of the internal feature mergeInternalFeatures( depIt->second.depends, feature, internalFeatures ); // Merge the internal feature's require data into the public feature feature.requireData.insert( feature.requireData.end(), std::make_move_iterator( depIt->second.requireData.begin() ), std::make_move_iterator( depIt->second.requireData.end() ) ); // Remove the internal feature to avoid re-processing internalFeatures.erase( depIt ); } } } bool VulkanHppGenerator::needsStructureChainResize( std::map const & vectorParams, std::vector const & chainedReturnParams ) const { auto it = std::ranges::find_if( chainedReturnParams, [&vectorParams]( size_t crp ) { return vectorParams.contains( crp ); } ); // assert that there's no other chained vector param ! assert( ( it == chainedReturnParams.end() ) || std::none_of( std::next( it ), chainedReturnParams.end(), [&vectorParams]( size_t crp ) { return vectorParams.contains( crp ); } ) ); return ( it != chainedReturnParams.end() ); } std::pair>> VulkanHppGenerator::needsVectorSizeCheck( std::vector const & params, std::map const & vectorParams, std::vector const & returnParams, std::set const & singularParams, std::set const & skippedParams ) const { std::map> countToVectorMap; for ( auto const & vpi : vectorParams ) { if ( ( vpi.second.lenParam != INVALID_INDEX ) && std::ranges::none_of( returnParams, [&vpi]( size_t rp ) { return rp == vpi.first; } ) && ( !singularParams.contains( vpi.second.lenParam ) || isLenByStructMember( params[vpi.first].lenExpression, params[vpi.second.lenParam] ) ) ) { countToVectorMap[vpi.second.lenParam].push_back( vpi.first ); } } return { std::ranges::any_of( countToVectorMap, [this, ¶ms, &skippedParams]( auto const & cvm ) { return ( 1 < cvm.second.size() ) || isLenByStructMember( params[cvm.second[0]].lenExpression, params[cvm.first] ) || std::ranges::none_of( params[cvm.second[0]].lenParams, [&skippedParams]( std::pair const & lenParam ) { return skippedParams.contains( lenParam.second ); } ); } ), countToVectorMap }; } void VulkanHppGenerator::readCommand( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); auto aliasIt = attributes.find( "alias" ); if ( aliasIt != attributes.end() ) { checkAttributes( line, attributes, { { "alias", {} }, { "name", {} } }, {} ); std::string alias = aliasIt->second; std::string name = attributes.find( "name" )->second; checkForError( name.starts_with( "vk" ), line, "name <" + name + "> should begin with \"vk\"" ); auto commandIt = m_commands.find( alias ); checkForError( commandIt != m_commands.end(), line, "command <" + name + "> is aliased to unknown command <" + alias + ">" ); checkForError( commandIt->second.aliases.insert( { name, line } ).second, line, "command <" + name + "> is already listed as alias for command <" + alias + ">" ); } else { checkAttributes( line, attributes, {}, { { "allownoqueues", { "true" } }, { "api", { "vulkan", "vulkanbase", "vulkansc" } }, { "cmdbufferlevel", { "primary", "secondary" } }, { "comment", {} }, { "conditionalrendering", { "false", "true" } }, { "errorcodes", {} }, { "export", { "vulkan", "vulkansc" } }, { "queues", {} }, { "renderpass", { "both", "inside", "outside" } }, { "successcodes", {} }, { "tasks", { "action", "indirection", "state", "synchronization" } }, { "videocoding", { "both", "inside", "outside" } } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "param", false }, { "proto", true } }, { "implicitexternsyncparams" } ); CommandData commandData; commandData.xmlLine = line; std::vector api; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { api = tokenize( attribute.second, "," ); } else if ( attribute.first == "errorcodes" ) { commandData.errorCodes = tokenize( attribute.second, "," ); // errorCodes are checked in checkCorrectness after complete reading } else if ( attribute.first == "export" ) { commandData.exports = tokenize( attribute.second, "," ); } else if ( attribute.first == "queues" ) { m_commandQueues.insert( attribute.second ); } else if ( attribute.first == "successcodes" ) { commandData.successCodes = tokenize( attribute.second, "," ); // successCodes are checked in checkCorrectness after complete reading } } std::string name; for ( auto child : children ) { std::string value = child->Value(); if ( value == "param" ) { std::pair result = readCommandParam( child, commandData.params ); if ( result.first ) { commandData.params.push_back( result.second ); } } else if ( value == "proto" ) { std::tie( name, commandData.returnType ) = readCommandProto( child, "vk" ); checkForError( commandData.returnType.postfix.empty(), line, "unexpected type postfix <" + commandData.returnType.postfix + ">" ); } } checkForError( api.empty() || commandData.exports.empty() || ( ( commandData.exports.size() == 1 ) && std::ranges::any_of( api, [&commandData]( auto const & a ) { return a == commandData.exports.front(); } ) ), line, "command <" + name + "> has disjunct attributes and " ); for ( auto & param : commandData.params ) { for ( auto & lenParam : param.lenParams ) { auto paramIt = findByName( commandData.params, lenParam.first ); checkForError( paramIt != commandData.params.end(), param.xmlLine, "param <" + param.name + "> uses unknown len parameter <" + lenParam.first + "> in its \"altlen\" attribute <" + param.lenExpression + ">" ); lenParam.second = std::distance( commandData.params.begin(), paramIt ); } if ( !param.strideParam.first.empty() ) { auto paramIt = findByName( commandData.params, param.strideParam.first ); checkForError( paramIt != commandData.params.end(), param.xmlLine, "param <" + param.name + "> uses unknown stride parameter <" + param.strideParam.first + ">" ); param.strideParam.second = std::distance( commandData.params.begin(), paramIt ); } } assert( !name.empty() ); checkForError( ( commandData.returnType.type == "VkResult" ) || commandData.errorCodes.empty(), line, "command <" + name + "> does not return a VkResult but specifies errorcodes" ); checkForError( ( commandData.returnType.type == "VkResult" ) || commandData.successCodes.empty(), line, "command <" + name + "> does not return a VkResult but specifies successcodes" ); if ( api.empty() || std::ranges::any_of( api, [this]( auto const & a ) { return a == m_api; } ) ) { checkForError( !m_commands.contains( name ), line, "command <" + name + "> already specified" ); addCommand( name, std::move( commandData ) ); } } } std::pair VulkanHppGenerator::readCommandParam( tinyxml2::XMLElement const * element, std::vector const & params ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, {}, { { "altlen", {} }, { "api", { "vulkan", "vulkanbase", "vulkansc" } }, { "externsync", {} }, { "len", {} }, { "noautovalidity", { "true" } }, { "objecttype", { "objectType" } }, { "optional", { "false", "true" } }, { "stride", {} }, { "validstructs", {} } } ); ParamData paramData; paramData.xmlLine = line; std::vector api; for ( auto attribute : attributes ) { if ( attribute.first == "altlen" ) { assert( paramData.lenExpression.empty() ); paramData.lenExpression = attribute.second; paramData.lenParams = filterNumbers( tokenizeAny( attribute.second, " /()+" ) ); } else if ( attribute.first == "api" ) { api = tokenize( attribute.second, "," ); } else if ( attribute.first == "len" ) { if ( paramData.lenExpression.empty() ) { paramData.lenExpression = attribute.second; auto paramIt = findByName( params, attribute.second ); if ( paramIt != params.end() ) { paramData.lenParams.push_back( { attribute.second, std::distance( params.begin(), paramIt ) } ); } else { checkForError( ( attribute.second == "null-terminated" ) || ( attribute.second == "1" ) || isLenByStructMember( attribute.second, params ), line, "attribute holds an unknown value <" + attribute.second + ">" ); } } } else if ( attribute.first == "stride" ) { paramData.strideParam.first = attribute.second; } else if ( attribute.first == "optional" ) { paramData.optional = ( attribute.second == "true" ); } else if ( attribute.first == "validstructs" ) { std::vector validStructs = tokenize( attribute.second, "," ); for ( auto const & vs : validStructs ) { checkForError( m_structs.contains( vs ), line, "unknown struct <" + vs + "> listed in attribute " ); } } } NameData nameData; std::tie( nameData, paramData.type ) = readNameAndType( element ); checkForError( paramData.type.prefix.empty() || ( paramData.type.prefix == "const" ) || ( paramData.type.prefix == "const struct" ) || ( paramData.type.prefix == "struct" ), line, "unexpected type prefix <" + paramData.type.prefix + ">" ); checkForError( paramData.type.postfix.empty() || ( paramData.type.postfix == "*" ) || ( paramData.type.postfix == "**" ) || ( paramData.type.postfix == "* const *" ), line, "unexpected type postfix <" + paramData.type.postfix + ">" ); checkForError( paramData.lenParams.empty() || paramData.type.isPointer(), line, "parameter <" + nameData.name + "> has an attribute but is not a pointer" ); paramData.name = nameData.name; paramData.arraySizes = nameData.arraySizes; bool valid = api.empty() || std::ranges::any_of( api, [this]( auto const & a ) { return a == m_api; } ); checkForError( !valid || std::ranges::none_of( params, [&name = nameData.name]( ParamData const & pd ) { return pd.name == name; } ), line, "command param <" + nameData.name + "> already used" ); return { valid, paramData }; } std::pair VulkanHppGenerator::readCommandProto( tinyxml2::XMLElement const * element, std::string const & prefix ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, {} ); auto [nameData, typeInfo] = readNameAndType( element ); checkForError( nameData.name.starts_with( prefix ), line, "name <" + nameData.name + "> does not begin with <" + prefix + ">" ); checkForError( nameData.arraySizes.empty(), line, "name <" + nameData.name + "> with unsupported arraySizes" ); checkForError( m_types.contains( typeInfo.type ), line, "unknown type <" + typeInfo.type + ">" ); checkForError( typeInfo.prefix.empty(), line, "unexpected type prefix <" + typeInfo.prefix + ">" ); return { nameData.name, typeInfo }; } void VulkanHppGenerator::readCommands( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, { { "comment", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "command", false } } ); for ( auto child : children ) { readCommand( child ); } } std::string VulkanHppGenerator::readComment( tinyxml2::XMLElement const * element ) const { return ::readComment( "VulkanHppGenerator", element ); } VulkanHppGenerator::DeprecateData VulkanHppGenerator::readDeprecateData( tinyxml2::XMLElement const * element ) const { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "explanationlink", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "command", "type" } ); DeprecateData deprecateData; deprecateData.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "explanationlink" ) { deprecateData.explanationLink = attribute.second; } } for ( auto child : children ) { const int childLine = child->GetLineNum(); std::string value = child->Value(); if ( value == "command" ) { deprecateData.commands.push_back( readName( child ) ); checkForError( m_commands.contains( deprecateData.commands.back() ), childLine, "deprecated command <" + deprecateData.commands.back() + "> is not listed as command" ); } else if ( value == "type" ) { deprecateData.types.push_back( readName( child ) ); checkForError( m_types.contains( deprecateData.types.back() ), childLine, "deprecated type <" + deprecateData.types.back() + "> is not listed as type" ); } } return deprecateData; } void VulkanHppGenerator::readEnums( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "bitwidth", { "64" } }, { "comment", {} }, { "type", { "bitmask", "constants", "enum" } } } ); std::vector children = getChildElements( element ); std::string bitwidth, name; std::string type = "constants"; // hard-coded fallback for older versions, where "API Constants" did not have a type attribute, yet for ( auto const & attribute : attributes ) { if ( attribute.first == "bitwidth" ) { bitwidth = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "type" ) { type = attribute.second; } } assert( !name.empty() ); if ( type == "constants" ) { assert( name == "API Constants" ); checkElements( line, children, { { "enum", false } }, {} ); for ( auto const & child : children ) { readEnumsConstants( child ); } } else { checkElements( line, children, {}, { "comment", "enum", "unused" } ); checkForError( !type.empty(), line, "enum without type" ); // get the EnumData entry in enum map auto enumIt = findByNameOrAlias( m_enums, name ); checkForError( enumIt != m_enums.end(), line, "enum <" + name + "> is not listed as enum in the types section" ); checkForError( enumIt->second.values.empty(), line, "enum <" + name + "> already holds values" ); // mark it as a bitmask, if it is one const bool bitmask = ( type == "bitmask" ); if ( bitmask ) { checkForError( name.find( "FlagBits" ) != std::string::npos, line, "bitmask <" + name + "> does not contain \"FlagBits\"" ); } enumIt->second.isBitmask = bitmask; enumIt->second.bitwidth = bitwidth; // read the names of the enum values for ( auto child : children ) { std::string value = child->Value(); if ( value == "enum" ) { readEnumsEnum( child, enumIt ); } } } } void VulkanHppGenerator::readEnumsConstants( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkElements( line, getChildElements( element ), {} ); std::string name, type, value; auto aliasIt = attributes.find( "alias" ); if ( aliasIt != attributes.end() ) { checkAttributes( line, attributes, { { "alias", {} }, { "name", {} } }, {} ); std::string alias; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; checkForError( !m_constants.contains( name ), line, "already specified enum constant <" + name + ">" ); } } auto constIt = m_constants.find( alias ); checkForError( constIt != m_constants.end(), line, "constant alias <" + name + "> is an alias of an unknown constant <" + alias + ">." ); type = constIt->second.type; value = constIt->second.value; } else { checkAttributes( line, attributes, { { "name", {} }, { "type", {} }, { "value", {} } }, { { "comment", {} } } ); for ( auto const & attribute : attributes ) { if ( attribute.first == "name" ) { name = attribute.second; checkForError( !m_constants.contains( name ), line, "already specified enum constant <" + name + ">" ); } else if ( attribute.first == "type" ) { type = attribute.second; } else if ( attribute.first == "value" ) { value = attribute.second; } } } checkForError( m_types.insert( { name, TypeData{ TypeCategory::Constant, {}, line } } ).second, line, "enum <" + name + "> already specified" ); m_constants[name] = { type, value, line }; } void VulkanHppGenerator::readEnumsEnum( tinyxml2::XMLElement const * element, std::map::iterator enumIt ) { const int line = element->GetLineNum(); bool deprecated = false; std::map attributes = getAttributes( element ); if ( attributes.contains( "alias" ) ) { checkAttributes( line, attributes, { { "alias", {} }, { "name", {} } }, { { "api", { "vulkan", "vulkansc" } }, { "comment", {} }, { "deprecated", { "aliased" } } } ); checkElements( line, getChildElements( element ), {} ); std::string alias, api, bitpos, name, value; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else if ( attribute.first == "api" ) { api = attribute.second; } else if ( attribute.first == "deprecated" ) { deprecated = true; } else if ( attribute.first == "name" ) { name = attribute.second; } } assert( !name.empty() ); if ( api.empty() || ( api == m_api ) ) { checkForError( enumIt->second.addEnumAlias( line, name, alias, "", true ), line, "enum value alias <" + name + "> already listed with different properties" ); } } else { checkAttributes( line, attributes, { { "name", {} } }, { { "bitpos", {} }, { "comment", {} }, { "value", {} } } ); checkElements( line, getChildElements( element ), {} ); std::string alias, bitpos, name, value; for ( auto const & attribute : attributes ) { if ( attribute.first == "bitpos" ) { bitpos = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "value" ) { value = attribute.second; } } std::string prefix = generateEnumSuffixes( enumIt->first, enumIt->second.isBitmask ).first; checkForError( name.starts_with( prefix ), line, "encountered enum value <" + name + "> that does not begin with expected prefix <" + prefix + ">" ); checkForError( bitpos.empty() ^ value.empty(), line, "both or none of \"bitpos\" and \"value\" are set for enum <" + name + "> which is invalid" ); checkForError( enumIt->second.addEnumValue( line, name, "", bitpos, value, true, deprecated ), line, "enum value <" + name + "> already listed with different properties" ); } } void VulkanHppGenerator::readExtensionRequire( tinyxml2::XMLElement const * element, ExtensionData & extensionData, bool extensionSupported ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, {}, { { "api", { "vulkan", "vulkansc" } }, { "comment", {} }, { "depends", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "command", "comment", "enum", "feature", "type" } ); RequireData requireData{ .xmlLine = line }; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { requireData.api = attribute.second; } else if ( attribute.first == "depends" ) { assert( requireData.depends.empty() ); requireData.depends = attribute.second; checkForWarning( std::ranges::none_of( extensionData.requireData, [&requireData]( RequireData const & rd ) { return ( rd.api == requireData.api ) && ( rd.depends == requireData.depends ); } ), line, "required dependency <" + requireData.depends + "> already listed for extension <" + extensionData.name + "> with the same api" ); } } const bool requireSupported = requireData.api.empty() || ( requireData.api == m_api ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "command" ) { requireData.commands.push_back( readRequireCommand( child ) ); auto commandIt = m_commands.find( requireData.commands.back().name ); if ( commandIt != m_commands.end() ) { checkForError( commandIt->second.exports.empty(), commandIt->second.xmlLine, "command <" + commandIt->first + "> is required by extension <" + extensionData.name + "> but is specified to be exported by some feature" ); } else { commandIt = findByNameOrAlias( m_commands, requireData.commands.back().name ); checkForError( commandIt != m_commands.end(), requireData.commands.back().xmlLine, "extension <" + extensionData.name + "> requires unknown command <" + requireData.commands.back().name + ">" ); } } else if ( value == "enum" ) { readRequireEnum( child, extensionData.name, extensionData.platform, extensionSupported && requireSupported, requireData ); } else if ( value == "feature" ) { requireData.features.push_back( readRequireFeature( child ) ); } else if ( value == "type" ) { requireData.types.push_back( readRequireType( child ) ); } } if ( !requireData.commands.empty() || !requireData.types.empty() || !requireData.enumConstants.empty() ) { if ( requireSupported ) { extensionData.requireData.push_back( requireData ); } else { extensionData.unsupportedRequireData.push_back( requireData ); } } } void VulkanHppGenerator::readExtensions( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, { { "comment", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "extension", false } } ); for ( auto child : children ) { readExtension( child ); } } enum class NodeType { AND, OR, VAR }; struct Node { NodeType type; std::string value; std::shared_ptr left; std::shared_ptr right; Node( NodeType t, const std::string & val ) : type( t ), value( val ), left( nullptr ), right( nullptr ) {} Node( NodeType t, std::shared_ptr l, std::shared_ptr r ) : type( t ), left( l ), right( r ) {} }; class Parser { public: Parser( const std::string & expr ) : expr( expr ), pos( 0 ) {} std::shared_ptr parse() { auto result = parse_or(); if ( pos != expr.length() ) { throw std::runtime_error( "Unexpected characters at end of expression" ); } return result; } private: std::string expr; size_t pos; std::shared_ptr parse_or() { auto left = parse_and(); while ( match( "," ) ) { auto right = parse_and(); left = std::make_shared( NodeType::OR, left, right ); } return left; } std::shared_ptr parse_and() { auto left = parse_primary(); while ( match( "+" ) ) { auto right = parse_primary(); left = std::make_shared( NodeType::AND, left, right ); } return left; } std::shared_ptr parse_primary() { if ( match( "(" ) ) { auto result = parse_or(); if ( !match( ")" ) ) { throw std::runtime_error( "Expected closing parenthesis" ); } return result; } return parse_variable(); } std::shared_ptr parse_variable() { size_t start = pos; while ( pos < expr.length() && ( std::isalnum( expr[pos] ) || ( expr[pos] == '_' ) ) ) { pos++; } if ( start == pos ) { throw std::runtime_error( "Expected variable" ); } return std::make_shared( NodeType::VAR, expr.substr( start, pos - start ) ); } bool match( const std::string & token ) { skip_whitespace(); if ( expr.substr( pos, token.length() ) == token ) { pos += token.length(); skip_whitespace(); return true; } return false; } void skip_whitespace() { while ( pos < expr.length() && std::isspace( expr[pos] ) ) { pos++; } } }; std::shared_ptr distribute_and_over_or( std::shared_ptr node ) { if ( node->type == NodeType::AND ) { if ( node->left->type == NodeType::OR ) { return std::make_shared( NodeType::OR, distribute_and_over_or( std::make_shared( NodeType::AND, node->left->left, node->right ) ), distribute_and_over_or( std::make_shared( NodeType::AND, node->left->right, node->right ) ) ); } else if ( node->right->type == NodeType::OR ) { return std::make_shared( NodeType::OR, distribute_and_over_or( std::make_shared( NodeType::AND, node->left, node->right->left ) ), distribute_and_over_or( std::make_shared( NodeType::AND, node->left, node->right->right ) ) ); } } return node; } std::shared_ptr normalize( std::shared_ptr node ) { if ( node->left ) { node->left = normalize( node->left ); } if ( node->right ) { node->right = normalize( node->right ); } if ( node->type == NodeType::AND ) { node = distribute_and_over_or( node ); } return node; } std::vector> vectorize( std::shared_ptr node ) { if ( node->type == NodeType::VAR ) { return { { node->value } }; } else if ( node->type == NodeType::OR ) { auto left = vectorize( node->left ); auto right = vectorize( node->right ); left.insert( left.end(), right.begin(), right.end() ); return left; } else { assert( node->type == NodeType::AND ); auto left = vectorize( node->left ); auto right = vectorize( node->right ); std::vector> result; for ( auto const & l : left ) { for ( auto const & r : right ) { result.push_back( l ); result.back().insert( result.back().end(), r.begin(), r.end() ); } } return result; } } void VulkanHppGenerator::readExtension( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); std::vector children = getChildElements( element ); checkAttributes( line, attributes, { { "name", {} }, { "number", {} }, { "supported", { "disabled", "vulkan", "vulkanbase", "vulkansc" } } }, { { "author", {} }, { "comment", {} }, { "contact", {} }, { "depends", {} }, { "deprecatedby", {} }, { "nofeatures", { "true" } }, { "obsoletedby", {} }, { "platform", {} }, { "promotedto", {} }, { "provisional", { "true" } }, { "ratified", { "vulkan", "vulkansc" } }, { "sortorder", { "1" } }, { "specialuse", { "cadsupport", "d3demulation", "debugging", "devtools", "glemulation" } }, { "type", { "device", "instance" } } } ); checkElements( line, children, { { "require", false } }, { "deprecate", "remove" } ); // some special handling for two extensions std::string name = attributes.find( "name" )->second; if ( ( name == "VK_EXT_fragment_density_map_offset" ) || ( name == "VK_KHR_swapchain_mutable_format" ) ) { auto dependsIt = attributes.find( "depends" ); assert( dependsIt != attributes.end() ); std::string depends = dependsIt->second; assert( ( name == "VK_EXT_fragment_density_map_offset" ) ? ( depends == "(VK_KHR_get_physical_device_properties2,VK_VERSION_1_1)+VK_EXT_fragment_density_map+(VK_KHR_create_renderpass2,VK_VERSION_1_2)+(VK_VERSION_1_3,VK_KHR_dynamic_rendering)" ) : ( depends == "VK_KHR_swapchain+(VK_KHR_maintenance2,VK_VERSION_1_1)+(VK_KHR_image_format_list,VK_VERSION_1_2)" ) ); dependsIt->second = ( name == "VK_EXT_fragment_density_map_offset" ) ? "VK_EXT_fragment_density_map+(((VK_KHR_get_physical_device_properties2,VK_VERSION_1_1)+VK_KHR_create_renderpass2,VK_VERSION_1_2)+VK_KHR_dynamic_rendering,VK_VERSION_1_3)" : "VK_KHR_swapchain+((VK_KHR_maintenance2,VK_VERSION_1_1)+VK_KHR_image_format_list,VK_VERSION_1_2)"; } ExtensionData extensionData{ .xmlLine = line }; for ( auto const & attribute : attributes ) { if ( attribute.first == "depends" ) { Parser parser( attribute.second ); std::vector> dependencies = vectorize( normalize( parser.parse() ) ); for ( auto & dep : dependencies ) { auto featureIt = std::ranges::find_if( dep, []( std::string const & d ) { return d.starts_with( "VK_VERSION" ); } ); if ( featureIt == dep.end() ) { dep.insert( dep.begin(), "VK_VERSION_1_0" ); } else if ( featureIt != dep.begin() ) { auto nextFeatureIt = std::find_if( std::next( featureIt ), dep.end(), []( std::string const & d ) { return d.starts_with( "VK_VERSION" ); } ); while ( nextFeatureIt != dep.end() ) { if ( *featureIt < *nextFeatureIt ) { featureIt = nextFeatureIt; } nextFeatureIt = std::find_if( std::next( nextFeatureIt ), dep.end(), []( std::string const & d ) { return d.starts_with( "VK_VERSION" ); } ); } std::iter_swap( featureIt, dep.begin() ); } } assert( std::ranges::all_of( dependencies, []( std::vector const & dep ) { return dep[0].starts_with( "VK_VERSION" ); } ) ); for ( auto & dep : dependencies ) { auto it = extensionData.depends.insert( { dep[0], {} } ).first; it->second.push_back( {} ); for ( auto depIt = std::next( dep.begin() ); depIt != dep.end(); ++depIt ) { if ( !depIt->starts_with( "VK_VERSION" ) ) { it->second.back().insert( *depIt ); } } } } else if ( attribute.first == "deprecatedby" ) { extensionData.deprecatedBy = attribute.second; extensionData.isDeprecated = true; } else if ( attribute.first == "name" ) { extensionData.name = attribute.second; checkForError( !isExtension( extensionData.name ), line, "already encountered extension <" + extensionData.name + ">" ); } else if ( attribute.first == "number" ) { extensionData.number = attribute.second; checkForError( isNumber( extensionData.number ), line, "extension number <" + extensionData.number + "> is not a number" ); checkForError( std::ranges::none_of( m_extensions, [&extensionData]( auto const & extension ) { return extension.number == extensionData.number; } ), line, "extension number <" + extensionData.number + "> already encountered" ); } else if ( attribute.first == "obsoletedby" ) { extensionData.obsoletedBy = attribute.second; } else if ( attribute.first == "platform" ) { extensionData.platform = attribute.second; checkForError( m_platforms.contains( extensionData.platform ), line, "unknown platform <" + extensionData.platform + ">" ); } else if ( attribute.first == "promotedto" ) { extensionData.promotedTo = attribute.second; } else if ( attribute.first == "provisional" ) { if ( extensionData.platform.empty() ) { // for now, having the attribute provisional="true" implies attribute platform="provisional" to get // stuff protected by VK_ENABLE_BETA_EXTENSIONS extensionData.platform = "provisional"; } checkForError( extensionData.platform == "provisional", line, "while attribute is set to \"true\", attribute is not set to \"provisional\" but to \"" + extensionData.platform + "\"" ); } else if ( attribute.first == "ratified" ) { extensionData.ratified = tokenize( attribute.second, "," ); } else if ( attribute.first == "supported" ) { extensionData.supported = tokenize( attribute.second, "," ); } else if ( attribute.first == "type" ) { extensionData.type = attribute.second; } } const bool extensionSupported = extensionData.supported.empty() || std::ranges::any_of( extensionData.supported, [this]( std::string const & s ) { return s == m_api; } ); checkForError( !extensionSupported || !extensionData.type.empty(), line, "missing attribute \"type\" for supported extension <" + extensionData.name + ">" ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "deprecate" ) { extensionData.deprecateData.push_back( readDeprecateData( child ) ); } else if ( value == "remove" ) { extensionData.removeData.push_back( readRemoveData( child ) ); } else if ( value == "require" ) { readExtensionRequire( child, extensionData, extensionSupported ); } } if ( std::ranges::none_of( extensionData.supported, []( std::string const & s ) { return s == "disabled"; } ) ) { // extract the tag from the name, which is supposed to look like VK__ const size_t tagStart = extensionData.name.find( '_' ); checkForError( tagStart != std::string::npos, line, "name <" + extensionData.name + "> is missing an underscore '_'" ); const size_t tagEnd = extensionData.name.find( '_', tagStart + 1 ); checkForError( tagEnd != std::string::npos, line, "name <" + extensionData.name + "> is missing an underscore '_'" ); std::string tag = extensionData.name.substr( tagStart + 1, tagEnd - tagStart - 1 ); checkForError( ( m_tags.find( tag ) != m_tags.end() ), line, "name <" + extensionData.name + "> is using an unknown tag <" + tag + ">" ); } if ( extensionSupported ) { m_extensions.push_back( extensionData ); } else { checkForError( !isUnsupportedExtension( extensionData.name ), line, "unsupported extension <" + extensionData.name + "> already specified" ); m_unsupportedExtensions.push_back( extensionData ); } } void VulkanHppGenerator::readFeature( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "api", { "vulkan", "vulkanbase", "vulkansc" } }, { "comment", {} }, { "name", {} }, { "number", {} } }, { { "apitype", { "internal" } }, { "depends", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "require", false } }, { "deprecate", "remove" } ); FeatureData featureData; featureData.xmlLine = line; std::string modifiedNumber; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { featureData.api = tokenize( attribute.second, "," ); } else if ( attribute.first == "depends" ) { featureData.depends = tokenize( attribute.second, "," ); for ( const auto & dep : featureData.depends ) { checkForError( findByName( m_features, dep ) != m_features.end(), featureData.xmlLine, "feature <" + featureData.name + "> depends on unknown feature <" + dep + ">" ); } } else if ( attribute.first == "name" ) { featureData.name = attribute.second; } else if ( attribute.first == "number" ) { featureData.number = attribute.second; modifiedNumber = featureData.number; std::replace( modifiedNumber.begin(), modifiedNumber.end(), '.', '_' ); } else if ( attribute.first == "apitype" ) { featureData.isInternal = ( attribute.second == "internal" ); } } const bool featureSupported = std::ranges::any_of( featureData.api, [this]( auto const & a ) { return a == m_api; } ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "deprecate" ) { featureData.deprecateData.push_back( readDeprecateData( child ) ); } else if ( value == "remove" ) { featureData.removeData.push_back( readRemoveData( child ) ); } else if ( value == "require" ) { readFeatureRequire( child, featureData, featureSupported ); } } if ( std::ranges::any_of( featureData.api, []( auto const & a ) { return a == "vulkanbase"; } ) ) { checkForError( ( featureData.name == "VK_BASE_VERSION_" + modifiedNumber ) || ( featureData.name == "VK_COMPUTE_VERSION_" + modifiedNumber ) || ( featureData.name == "VK_GRAPHICS_VERSION_" + modifiedNumber ) || ( featureData.name == "VK_VERSION_" + modifiedNumber ), line, "unexpected formatting of name <" + featureData.name + ">" ); } else { checkForError( featureData.name == ( std::ranges::any_of( featureData.api, []( auto const & a ) { return a == "vulkan"; } ) ? "VK_VERSION_" : "VKSC_VERSION_" ) + modifiedNumber, line, "unexpected formatting of name <" + featureData.name + ">" ); } checkForError( !isFeature( featureData.name ), line, "feature <" + featureData.name + "> already specified" ); if ( featureSupported ) { m_features.push_back( featureData ); } else { m_unsupportedFeatures.push_back( featureData ); } } void VulkanHppGenerator::readFeatureRequire( tinyxml2::XMLElement const * element, FeatureData & featureData, bool featureSupported ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, {}, { { "comment", {} }, { "depends", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "command", "comment", "enum", "feature", "type" } ); std::string depends; for ( auto const & attribute : attributes ) { if ( attribute.first == "depends" ) { assert( depends.empty() ); depends = attribute.second; checkForError( std::ranges::none_of( featureData.requireData, [&depends]( RequireData const & rd ) { return rd.depends == depends; } ), line, "require depends <" + depends + "> already listed for feature <" + featureData.name + ">" ); } } featureData.requireData.push_back( {} ); RequireData & requireData = featureData.requireData.back(); requireData.depends = depends; requireData.xmlLine = line; for ( auto child : children ) { std::string value = child->Value(); if ( value == "command" ) { requireData.commands.push_back( readRequireCommand( child ) ); auto commandIt = m_commands.find( requireData.commands.back().name ); checkForError( commandIt != m_commands.end(), requireData.commands.back().xmlLine, "feature <" + featureData.name + "> requires unknown command <" + requireData.commands.back().name + ">" ); checkForError( ( std::stoi( m_version ) < 319 ) || !commandIt->second.exports.empty(), commandIt->second.xmlLine, "command <" + commandIt->first + "> is required by feature <" + featureData.name + "> but is not marked as exported" ); checkForError( commandIt->second.exports.empty() || std::ranges::any_of( commandIt->second.exports, [&featureData]( auto const & api ) { return std::ranges::find( featureData.api, api ) != featureData.api.end(); } ), commandIt->second.xmlLine, "command <" + commandIt->first + "> is required by feature <" + featureData.name + "> but is not exported for the feature's api" ); } else if ( value == "enum" ) { readRequireEnum( child, featureData.name, "", featureSupported, requireData ); } else if ( value == "feature" ) { requireData.features.push_back( readRequireFeature( child ) ); } else if ( value == "type" ) { requireData.types.push_back( readRequireType( child ) ); } } checkForError( requireData.enumConstants.empty(), line, "Need to handle enumConstants in features as well!" ); } void VulkanHppGenerator::readFormat( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "blockSize", { "1", "2", "3", "4", "5", "6", "8", "12", "16", "24", "27", "32", "36", "48", "64", "80", "100", "125", "150", "180", "216" } }, { "class", {} }, { "name", {} }, { "texelsPerBlock", { "1", "16", "20", "25", "27", "30", "36", "40", "48", "50", "60", "64", "80", "100", "120", "125", "144", "150", "180", "216" } } }, { { "blockExtent", { "1", "2", "3", "4", "5", "6", "8", "10", "12" } }, { "chroma", { "420", "422", "444" } }, { "compressed", { "ASTC HDR", "ASTC LDR", "BC", "EAC", "ETC", "ETC2", "PVRTC" } }, { "packed", { "8", "16", "32" } } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "component", false } }, { "plane", "spirvimageformat" } ); FormatData format; format.xmlLine = line; std::string name; for ( auto const & attribute : attributes ) { if ( attribute.first == "blockExtent" ) { checkForError( tokenize( attribute.second, "," ).size() == 3, line, "unexpected number of elements in attribute " ); format.blockExtent = attribute.second; } if ( attribute.first == "blockSize" ) { format.blockSize = attribute.second; } else if ( attribute.first == "chroma" ) { format.chroma = attribute.second; } else if ( attribute.first == "class" ) { format.classAttribute = attribute.second; } else if ( attribute.first == "compressed" ) { format.compressed = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "packed" ) { format.packed = attribute.second; } else if ( attribute.first == "texelsPerBlock" ) { format.texelsPerBlock = attribute.second; } } for ( auto child : children ) { std::string value = child->Value(); if ( value == "component" ) { readFormatComponent( child, format ); } else if ( value == "plane" ) { readFormatPlane( child, format ); } else if ( value == "spirvimageformat" ) { readFormatSPIRVImageFormat( child, format ); } } if ( format.components.front().bits == "compressed" ) { for ( auto componentIt = std::next( format.components.begin() ); componentIt != format.components.end(); ++componentIt ) { checkForError( componentIt->bits == "compressed", line, "component is expected to be marked as compressed in attribute " ); } } if ( !format.components.front().planeIndex.empty() ) { for ( auto componentIt = std::next( format.components.begin() ); componentIt != format.components.end(); ++componentIt ) { checkForError( !componentIt->planeIndex.empty(), line, "component is expected to have a planeIndex" ); } const size_t planeCount = 1 + static_cast( std::stoi( format.components.back().planeIndex ) ); checkForError( format.planes.size() == planeCount, line, "number of planes does not fit to largest planeIndex of the components" ); } auto formatIt = m_enums.find( "VkFormat" ); assert( formatIt != m_enums.end() ); checkForError( containsName( formatIt->second.values, name ) && m_formats.insert( { name, format } ).second, line, "format <" + name + "> already specified" ); } void VulkanHppGenerator::readFormatComponent( tinyxml2::XMLElement const * element, FormatData & formatData ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "bits", { "1", "2", "4", "5", "6", "8", "9", "10", "11", "12", "14", "16", "24", "32", "64", "compressed" } }, { "name", {} }, { "numericFormat", { "BOOL", "SFLOAT", "SINT", "SNORM", "SRGB", "SFIXED5", "SSCALED", "UFLOAT", "UINT", "UNORM", "USCALED" } } }, { { "planeIndex", { "0", "1", "2" } } } ); checkElements( line, getChildElements( element ), {} ); formatData.components.emplace_back(); ComponentData & component = formatData.components.back(); component.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "bits" ) { checkForError( ( attribute.second != "compressed" ) || !formatData.compressed.empty(), line, "component of a not compressed format is marked as compressed" ); component.bits = attribute.second; } else if ( attribute.first == "name" ) { component.name = attribute.second; } else if ( attribute.first == "numericFormat" ) { component.numericFormat = attribute.second; } else if ( attribute.first == "planeIndex" ) { component.planeIndex = attribute.second; } } } void VulkanHppGenerator::readFormatPlane( tinyxml2::XMLElement const * element, FormatData & formatData ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "compatible", {} }, { "index", { "0", "1", "2" } }, { "heightDivisor", { "1", "2" } }, { "widthDivisor", { "1", "2" } } }, {} ); checkElements( line, getChildElements( element ), {} ); formatData.planes.emplace_back(); PlaneData & plane = formatData.planes.back(); plane.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "compatible" ) { plane.compatible = attribute.second; auto formatIt = m_enums.find( "VkFormat" ); assert( formatIt != m_enums.end() ); checkForError( containsName( formatIt->second.values, plane.compatible ), line, "encountered unknown format <" + plane.compatible + ">" ); } else if ( attribute.first == "index" ) { const size_t index = std::stoi( attribute.second ); checkForError( index + 1 == formatData.planes.size(), line, "unexpected index <" + attribute.second + ">" ); } else if ( attribute.first == "heightDivisor" ) { plane.heightDivisor = attribute.second; } else if ( attribute.first == "widthDivisor" ) { plane.widthDivisor = attribute.second; } } } void VulkanHppGenerator::readFormats( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, { { "comment", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "format", false } } ); for ( auto child : children ) { readFormat( child ); } } void VulkanHppGenerator::readFormatSPIRVImageFormat( tinyxml2::XMLElement const * element, FormatData & formatData ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string name = attributes.find( "name" )->second; checkForError( formatData.spirvImageFormat.empty(), line, "spirvimageformat <" + name + "> already specified" ); formatData.spirvImageFormat = name; } std::pair, std::string> VulkanHppGenerator::readModifiers( tinyxml2::XMLNode const * node ) const { return ::readModifiers( "VulkanHppGenerator", node ); } std::string VulkanHppGenerator::readName( tinyxml2::XMLElement const * element ) const { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); return attributes.find( "name" )->second; } std::pair VulkanHppGenerator::readNameAndType( tinyxml2::XMLElement const * element ) { int line = element->GetLineNum(); std::vector children = getChildElements( element ); checkElements( line, children, { { "name", true } }, { { "enum" }, { "type" } } ); NameData nameData; TypeInfo typeInfo; for ( auto child : children ) { line = child->GetLineNum(); checkAttributes( line, getAttributes( child ), {}, {} ); checkElements( line, getChildElements( child ), {} ); std::string value = child->Value(); if ( value == "enum" ) { nameData.arraySizes.push_back( child->GetText() ); checkForError( child->PreviousSibling() && ( strcmp( child->PreviousSibling()->Value(), "[" ) == 0 ) && child->NextSibling() && ( strcmp( child->NextSibling()->Value(), "]" ) == 0 ), line, std::string( "array specifiation is ill-formatted: <" ) + nameData.arraySizes.back() + ">" ); checkForError( m_constants.contains( nameData.arraySizes.back() ), line, "using unknown enum value <" + nameData.arraySizes.back() + ">" ); } else if ( value == "name" ) { nameData.name = child->GetText(); std::string bitCount; std::tie( nameData.arraySizes, bitCount ) = readModifiers( child->NextSibling() ); checkForError( bitCount.empty(), line, "name <" + nameData.name + "> with unsupported bitCount <" + bitCount + ">" ); } else if ( value == "type" ) { typeInfo = readTypeInfo( child ); } } return { nameData, typeInfo }; } void VulkanHppGenerator::readPlatform( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "comment", {} }, { "name", {} }, { "protect", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string name, protect; for ( auto const & attribute : attributes ) { if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "protect" ) { protect = attribute.second; } } checkForError( std::ranges::none_of( m_platforms, [&protect]( std::pair const & p ) { return p.second.protect == protect; } ), line, "protect <" + protect + "> of platform <" + name + "> is already used with some other platform" ); checkForError( m_platforms.insert( { name, { protect, line } } ).second, line, "platform <" + name + "> is already specified" ); } void VulkanHppGenerator::readPlatforms( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), { { "comment", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, { { "platform", false } } ); for ( auto child : children ) { readPlatform( child ); } } void VulkanHppGenerator::readRegistry( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, {} ); std::vector children = getChildElements( element ); checkElements( line, children, { { "commands", false }, { "comment", false }, { "enums", false }, { "extensions", false }, { "feature", false }, { "formats", false }, { "platforms", true }, { "spirvcapabilities", true }, { "spirvextensions", true }, { "sync", true }, { "tags", true }, { "types", false } }, { "videocodecs" } ); // make this optional for now, make it required around October 2024 for ( auto child : children ) { const std::string value = child->Value(); if ( value == "commands" ) { readCommands( child ); } else if ( value == "comment" ) { std::string comment = readComment( child ); if ( comment.find( "\nCopyright" ) == 0 ) { m_vulkanLicenseHeader = generateCopyrightMessage( comment ); } } else if ( value == "enums" ) { readEnums( child ); } else if ( value == "extensions" ) { readExtensions( child ); } else if ( value == "feature" ) { readFeature( child ); } else if ( value == "formats" ) { readFormats( child ); } else if ( value == "platforms" ) { readPlatforms( child ); } else if ( value == "spirvcapabilities" ) { readSPIRVCapabilities( child ); } else if ( value == "spirvextensions" ) { readSPIRVExtensions( child ); } else if ( value == "sync" ) { readSync( child ); } else if ( value == "tags" ) { readTags( child ); } else if ( value == "types" ) { readTypes( child ); } else if ( value == "videocodecs" ) { readVideoCodecs( child ); } } if ( m_api != "vulkanbase" ) { // for all api but vulkanbase, we merge the internal features into the normal ones mergeInternalFeatures(); } distributeEnumValueAliases(); distributeStructAliases(); distributeRequirements(); markExtendedStructs(); } VulkanHppGenerator::RemoveData VulkanHppGenerator::readRemoveData( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, { { "comment", {} }, { "reasonlink", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "command", "enum", "feature", "type" } ); RemoveData removeData; removeData.xmlLine = line; for ( auto child : children ) { std::string value = child->Value(); if ( value == "command" ) { removeData.commands.push_back( readName( child ) ); } else if ( value == "enum" ) { removeData.enums.push_back( readName( child ) ); } else if ( value == "feature" ) { removeData.features.push_back( readRequireFeature( child ) ); } else if ( value == "type" ) { removeData.types.push_back( readName( child ) ); } } return removeData; } VulkanHppGenerator::NameLine VulkanHppGenerator::readRequireCommand( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "comment", {} } } ); checkElements( line, getChildElements( element ), {} ); return { attributes.find( "name" )->second, line }; } void VulkanHppGenerator::readRequireEnum( tinyxml2::XMLElement const * element, std::string const & requiredBy, std::string const & platform, bool supported, RequireData & requireData ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkElements( line, getChildElements( element ), {} ); if ( attributes.contains( "alias" ) ) { checkAttributes( line, attributes, { { "alias", {} }, { "name", {} } }, { { "api", { "vulkan", "vulkansc" } }, { "comment", {} }, { "deprecated", { "aliased" } }, { "extends", {} }, { "protect", { "VK_ENABLE_BETA_EXTENSIONS" } } } ); std::string alias, api, extends, name, protect; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else if ( attribute.first == "api" ) { api = attribute.second; } else if ( attribute.first == "extends" ) { extends = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "protect" ) { protect = attribute.second; } } if ( extends.empty() ) { checkForError( protect.empty(), line, "protect <" + protect + "> of enum alias <" + alias + "> with empty is not expected" ); // enum aliases that don't extend something are listed as constants auto typeIt = m_types.find( alias ); checkForError( typeIt != m_types.end(), line, "enum alias <" + name + "> is an alias of an unknown enum <" + alias + ">" ); checkForError( typeIt->second.category == TypeCategory::Constant, line, "enum alias <" + name + "> is an alias of a non-constant type <" + alias + ">" ); checkForError( m_types.insert( { name, TypeData{ TypeCategory::Constant, { requiredBy }, line } } ).second, line, "required enum <" + name + "> already specified" ); auto constIt = m_constants.find( alias ); if ( constIt != m_constants.end() ) { typeIt = m_types.find( name ); typeIt->second.requiredBy.insert( requiredBy ); m_constants[name] = { constIt->second.type, constIt->second.value, line }; requireData.constants.push_back( { name, line } ); } } else { if ( protect.empty() ) { protect = getProtectFromPlatform( platform ); } auto typeIt = m_types.find( extends ); checkForError( typeIt != m_types.end(), line, "enum value <" + name + "> extends unknown type <" + extends + ">" ); checkForError( typeIt->second.category == TypeCategory::Enum, line, "enum value <" + name + "> extends non-enum type <" + extends + ">" ); typeIt->second.requiredBy.insert( requiredBy ); auto enumIt = findByNameOrAlias( m_enums, extends ); assert( enumIt != m_enums.end() ); checkForError( enumIt->second.addEnumAlias( line, name, alias, protect, supported ), line, "enum value alias <" + name + "> already listed with different properties" ); } } else { checkAttributes( line, attributes, { { "name", {} } }, { { "api", { "vulkan", "vulkansc" } }, { "bitpos", {} }, { "comment", {} }, { "deprecated", { "true" } }, { "dir", { "-" } }, { "extends", {} }, { "extnumber", {} }, { "offset", {} }, { "protect", { "VK_ENABLE_BETA_EXTENSIONS" } }, { "type", { "float", "uint32_t" } }, { "value", {} } } ); std::string api, bitpos, extends, name, offset, protect, type, value; bool deprecated = false; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { api = attribute.second; } else if ( attribute.first == "bitpos" ) { bitpos = attribute.second; } else if ( attribute.first == "deprecated" ) { deprecated = true; } else if ( attribute.first == "extends" ) { extends = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; checkForError( name.starts_with( "VK_" ), line, "enum value <" + name + "> does not start with " ); } else if ( attribute.first == "offset" ) { offset = attribute.second; } else if ( attribute.first == "protect" ) { protect = attribute.second; } else if ( attribute.first == "type" ) { type = attribute.second; } else if ( attribute.first == "value" ) { value = attribute.second; } } if ( extends.empty() ) { checkForError( protect.empty(), line, "protect <" + protect + "> of required enum <" + name + "> with empty is not expected" ); if ( value.empty() ) { auto typeIt = m_types.find( name ); checkForError( typeIt != m_types.end(), line, "unknown required enum <" + name + ">" ); typeIt->second.requiredBy.insert( requiredBy ); requireData.constants.push_back( { name, line } ); } else { if ( api.empty() || ( api == m_api ) ) { checkForError( m_types.insert( { name, TypeData{ TypeCategory::Constant, { requiredBy }, line } } ).second, line, "required enum <" + name + "> specified by value <" + value + "> is already specified" ); if ( !type.empty() ) { assert( !m_constants.contains( name ) ); m_constants[name] = { type, value, line }; } } requireData.enumConstants.push_back( { name, value, line } ); } } else { checkForError( bitpos.empty() + offset.empty() + value.empty() == 2, line, "exactly one out of bitpos = <" + bitpos + ">, offset = <" + offset + ">, and value = <" + value + "> are supposed to be empty" ); auto typeIt = m_types.find( extends ); checkForError( typeIt != m_types.end(), line, "enum value <" + name + "> extends unknown type <" + extends + ">" ); checkForError( typeIt->second.category == TypeCategory::Enum, line, "enum value <" + name + "> extends non-enum type <" + extends + ">" ); typeIt->second.requiredBy.insert( requiredBy ); auto enumIt = findByNameOrAlias( m_enums, extends ); assert( enumIt != m_enums.end() ); checkForError( enumIt->second.addEnumValue( line, name, protect.empty() ? getProtectFromPlatform( platform ) : protect, bitpos + offset, value, ( api.empty() || ( api == m_api ) ) && supported, deprecated ), line, "enum value <" + name + "> already listed with different properties" ); } } } VulkanHppGenerator::RequireFeature VulkanHppGenerator::readRequireFeature( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} }, { "struct", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::vector name; std::string structure; for ( auto const & attribute : attributes ) { if ( attribute.first == "name" ) { name = tokenize( attribute.second, "," ); } else if ( attribute.first == "struct" ) { structure = attribute.second; } } assert( !name.empty() && !structure.empty() ); auto structIt = m_structs.find( structure ); if ( structIt == m_structs.end() ) { auto aliasIt = std::ranges::find_if( m_structsAliases, [&structure]( std::pair const & ad ) { return ad.first == structure; } ); checkForError( aliasIt != m_structsAliases.end(), line, "encountered unknown required feature struct <" + structure + ">" ); auto nextAliasIt = aliasIt; do { nextAliasIt = std::ranges::find_if( m_structsAliases, [&structure = aliasIt->second.name]( std::pair const & ad ) { return ad.first == structure; } ); if ( nextAliasIt != m_structsAliases.end() ) { aliasIt = nextAliasIt; } } while ( nextAliasIt != m_structsAliases.end() ); structIt = m_structs.find( aliasIt->second.name ); assert( structIt != m_structs.end() ); } for ( auto const & n : name ) { auto memberIt = std::ranges::find_if( structIt->second.members, [&n]( MemberData const & md ) { return md.name == n; } ); checkForError( memberIt != structIt->second.members.end(), line, "required feature name <" + n + "> not part of the required feature struct <" + structure + ">" ); checkForError( ( memberIt->type.isValue() && ( memberIt->type.type == "VkBool32" ) ), line, "required feature name <" + n + "> is not a VkBool32 member of the required feature struct <" + structure + ">" ); } return { name, structure, line }; } VulkanHppGenerator::NameLine VulkanHppGenerator::readRequireType( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "comment", {} } } ); checkElements( line, getChildElements( element ), {} ); return { attributes.find( "name" )->second, line }; } void VulkanHppGenerator::readSPIRVCapability( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "enable" } ); std::string name = attributes.find( "name" )->second; auto [capabilityIt, inserted] = m_spirVCapabilities.insert( { name, { {}, line } } ); checkForError( inserted, line, "spirvcapability <" + name + "> already listed" ); for ( auto child : children ) { readSPIRVCapabilityEnable( child, capabilityIt->second ); } } void VulkanHppGenerator::readSPIRVCapabilityEnable( tinyxml2::XMLElement const * element, SpirVCapabilityData & capability ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkElements( line, getChildElements( element ), {}, {} ); if ( attributes.contains( "extension" ) ) { checkAttributes( line, attributes, { { "extension", {} } }, {} ); std::string const & extension = attributes.find( "extension" )->second; checkForError( isExtension( extension ), line, "unknown extension <" + extension + "> specified for SPIR-V capability" ); } else if ( attributes.contains( "property" ) ) { checkAttributes( line, attributes, { { "member", {} }, { "property", {} }, { "requires", {} }, { "value", {} } }, {} ); std::string member, property, value; for ( auto const & attribute : attributes ) { if ( attribute.first == "member" ) { member = attribute.second; } else if ( attribute.first == "property" ) { property = attribute.second; } if ( attribute.first == "requires" ) { std::vector require = tokenize( attribute.second, "," ); for ( auto const & r : require ) { checkForError( isFeature( r ) || isExtension( r ), line, "unknown requires <" + r + "> specified for SPIR-V capability" ); } } else if ( attribute.first == "value" ) { value = attribute.second; } } auto propertyIt = m_structs.find( property ); checkForError( propertyIt != m_structs.end(), line, "unknown property <" + property + "> specified for SPIR-V capability" ); auto memberIt = findByName( propertyIt->second.members, member ); checkForError( memberIt != propertyIt->second.members.end(), line, "unknown member <" + member + "> specified for SPIR-V capability" ); if ( memberIt->type.type == "VkBool32" ) { checkForError( ( value == "VK_FALSE" ) || ( value == "VK_TRUE" ), line, "unknown value <" + value + "> for boolean member <" + member + "> specified for SPIR-V capability" ); } else { auto bitmaskIt = m_bitmasks.find( memberIt->type.type ); checkForError( bitmaskIt != m_bitmasks.end(), line, "member <" + member + "> specified for SPIR-V capability is not a bitmask" ); checkForError( !bitmaskIt->second.require.empty(), line, "member <" + member + "> specified for SPIR-V capability has no required enum" ); const auto enumIt = m_enums.find( bitmaskIt->second.require ); checkForError( enumIt != m_enums.end(), line, "member <" + member + "> specified for SPIR-V capability requires an unknown enum <" + bitmaskIt->second.require + ">" ); checkForError( containsByName( enumIt->second.values, value ) || containsByName( enumIt->second.valueAliases, value ), line, "unknown attribute value <" + value + "> specified for SPIR-V capability" ); } } else if ( attributes.contains( "struct" ) ) { checkAttributes( line, attributes, { { "feature", {} }, { "struct", {} } }, { { "alias", {} }, { "requires", {} } } ); std::string feature, structure; for ( auto const & attribute : attributes ) { if ( attribute.first == "feature" ) { feature = attribute.second; } else if ( attribute.first == "requires" ) { std::vector require = tokenize( attribute.second, "," ); for ( auto const & r : require ) { checkForError( isFeature( r ) || isExtension( r ), line, "unknown requires <" + r + "> specified for SPIR-V capability" ); } } else if ( attribute.first == "struct" ) { structure = attribute.second; } } auto structIt = capability.structs.insert( { structure, {} } ).first; checkForError( structIt->second.insert( { feature, line } ).second, line, "feature <" + feature + "> already specified for struct <" + structure + "> for SPIR-V capability" ); } else if ( attributes.contains( "version" ) ) { checkAttributes( line, attributes, { { "version", {} } }, {} ); std::string version = attributes.find( "version" )->second; if ( version.starts_with( "VK_API_" ) ) { version.erase( 3, 4 ); // remove "API_" from the version -> VK_VERSION_x_y } checkForError( isFeature( version ), line, "unknown version <" + version + "> specified for SPIR-V capability" ); } } void VulkanHppGenerator::readSPIRVCapabilities( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "comment", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "spirvcapability" } ); for ( auto child : children ) { readSPIRVCapability( child ); } } void VulkanHppGenerator::readSPIRVExtension( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "enable" } ); for ( auto child : children ) { readSPIRVExtensionEnable( child ); } } void VulkanHppGenerator::readSPIRVExtensionEnable( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, {}, { { "extension", {} }, { "version", {} } } ); checkElements( line, getChildElements( element ), {}, {} ); for ( auto const & attribute : attributes ) { if ( attribute.first == "extension" ) { checkForError( isExtension( attribute.second ), line, "unknown extension <" + attribute.second + "> specified for SPIR-V extension" ); } else { assert( attribute.first == "version" ); std::string feature = attribute.second; if ( feature.starts_with( "VK_API_" ) ) { feature.erase( 3, 4 ); // remove "API_" from the version -> VK_VERSION_x_y } checkForError( isFeature( feature ), line, "unknown version <" + attribute.second + "> specified for SPIR-V extension" ); } } } void VulkanHppGenerator::readSPIRVExtensions( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "comment", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "spirvextension" } ); for ( auto child : children ) { readSPIRVExtension( child ); } } void VulkanHppGenerator::readStructMember( tinyxml2::XMLElement const * element, std::vector & members, bool isUnion ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, {}, { { "altlen", {} }, { "api", { "vulkan", "vulkanbase", "vulkansc" } }, { "deprecated", { "ignored" } }, { "externsync", { "maybe", "true" } }, { "featurelink", {} }, { "len", {} }, { "limittype", { "bitmask", "bits", "exact", "max", "min", "mul", "noauto", "not", "pot", "range", "struct" } }, { "noautovalidity", { "true" } }, { "objecttype", { "objectType" } }, { "optional", { "false", "true" } }, { "selection", {} }, { "selector", {} }, { "values", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "name", true }, { "type", true } }, { "comment", "enum" } ); MemberData memberData; memberData.xmlLine = line; std::vector api; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { api = tokenize( attribute.second, "," ); } else if ( attribute.first == "altlen" ) { assert( memberData.lenExpressions.empty() ); memberData.lenExpressions = tokenize( attribute.second, "," ); checkForError( memberData.lenExpressions.size() == 1, line, "member attribute holds unknown number of data: " + std::to_string( memberData.lenExpressions.size() ) ); memberData.lenMembers = filterNumbers( tokenizeAny( attribute.second, " /()+*" ) ); } else if ( attribute.first == "deprecated" ) { memberData.deprecated = attribute.second; } else if ( attribute.first == "len" ) { if ( memberData.lenExpressions.empty() ) { memberData.lenExpressions = tokenize( attribute.second, "," ); checkForError( !memberData.lenExpressions.empty() && ( memberData.lenExpressions.size() <= 2 ), line, "member attribute holds unknown number of data: " + std::to_string( memberData.lenExpressions.size() ) ); if ( 1 < memberData.lenExpressions.size() ) { checkForError( ( memberData.lenExpressions[1] == "1" ) || ( memberData.lenExpressions[1] == "null-terminated" ), line, "member attribute holds unknown second value <" + memberData.lenExpressions[1] + ">" ); } } } else if ( attribute.first == "limitype" ) { memberData.limitType = attribute.second; } else if ( attribute.first == "noautovalidity" ) { memberData.noAutoValidity = ( attribute.second == "true" ); } else if ( attribute.first == "optional" ) { std::vector optional = tokenize( attribute.second, "," ); memberData.optional.reserve( optional.size() ); for ( auto const & o : optional ) { memberData.optional.push_back( o == "true" ); } } else if ( attribute.first == "selection" ) { checkForError( isUnion, line, "attribute is used with a non-union structure." ); memberData.selection = tokenize( attribute.second, "," ); } else if ( attribute.first == "selector" ) { memberData.selector = attribute.second; auto selectorIt = findByName( members, memberData.selector ); checkForError( selectorIt != members.end(), line, "member attribute holds unknown value <" + memberData.selector + ">" ); checkForError( containsByNameOrAlias( m_enums, selectorIt->type.type ), line, "member attribute references unknown enum type <" + selectorIt->type.type + ">" ); } else if ( attribute.first == "values" ) { std::vector values = tokenize( attribute.second, "," ); checkForError( values.size() == 1, line, "attribute \"values\" holds multiple values <" + attribute.first + ">, but it's expected to hold just one" ); memberData.value = values[0]; } } std::string name; for ( auto child : children ) { const int childLine = child->GetLineNum(); checkAttributes( childLine, getAttributes( child ), {}, {} ); checkElements( childLine, getChildElements( child ), {}, {} ); std::string value = child->Value(); if ( value == "enum" ) { std::string enumString = child->GetText(); checkForError( child->PreviousSibling() && ( strcmp( child->PreviousSibling()->Value(), "[" ) == 0 ) && child->NextSibling() && ( strcmp( child->NextSibling()->Value(), "]" ) == 0 ), line, std::string( "struct member array specifiation is ill-formatted: <" ) + enumString + ">" ); memberData.arraySizes.push_back( enumString ); } else if ( value == "name" ) { name = child->GetText(); std::tie( memberData.arraySizes, memberData.bitCount ) = readModifiers( child->NextSibling() ); } else if ( value == "type" ) { memberData.type = readTypeInfo( child ); } } assert( !name.empty() ); checkForError( ( memberData.type.postfix.length() < 3 ) || !memberData.type.postfix.starts_with( "[" ) || !memberData.type.postfix.ends_with( "]" ), line, "struct member <" + name + "> has its array size <" + memberData.type.postfix + "> at the wrong position" ); if ( api.empty() || std::ranges::any_of( api, [this]( auto const & a ) { return a == m_api; } ) ) { checkForError( std::ranges::none_of( members, [&name]( MemberData const & md ) { return md.name == name; } ), line, "struct member name <" + name + "> already used" ); memberData.name = name; members.push_back( memberData ); } } void VulkanHppGenerator::readSync( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), { { "comment", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, { { "syncaccess", false }, { "syncpipeline", false }, { "syncstage", false } }, {} ); auto accessFlagBits2It = m_enums.find( "VkAccessFlagBits2" ); assert( accessFlagBits2It != m_enums.end() ); auto stageFlagBits2It = m_enums.find( "VkPipelineStageFlagBits2" ); assert( stageFlagBits2It != m_enums.end() ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "syncaccess" ) { readSyncAccess( child, accessFlagBits2It ); } else if ( value == "syncpipeline" ) { readSyncPipeline( child ); } else { assert( value == "syncstage" ); readSyncStage( child, stageFlagBits2It ); } } } void VulkanHppGenerator::readSyncAccess( tinyxml2::XMLElement const * element, std::map::const_iterator accessFlagBits2It ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "alias", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "comment", "syncequivalent", "syncsupport" } ); std::string alias, name; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else { assert( attribute.first == "name" ); name = attribute.second; } } checkForError( m_syncAccesses.insert( { name, { alias, line } } ).second, line, "syncaccess <" + name + "> already listed" ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "syncequivalent" ) { readSyncAccessEquivalent( child, accessFlagBits2It ); } else if ( value == "syncsupport" ) { readSyncAccessSupport( child ); } } } void VulkanHppGenerator::readSyncAccessEquivalent( tinyxml2::XMLElement const * element, std::map::const_iterator accessFlagBits2It ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "access", {} } }, {} ); checkElements( line, getChildElements( element ), {}, {} ); for ( auto const & attribute : attributes ) { assert( attribute.first == "access" ); std::vector access = tokenize( attribute.second, "," ); for ( auto const & a : access ) { checkForError( containsName( accessFlagBits2It->second.values, a ), line, "syncequivalent access uses unknown value <" + a + ">!" ); } } } void VulkanHppGenerator::readSyncAccessSupport( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "stage", {} } }, {} ); checkElements( line, getChildElements( element ), {}, {} ); std::vector stages = tokenize( attributes.find( "stage" )->second, "," ); for ( auto const & stage : stages ) { checkForError( m_syncStages.contains( stage ), line, "syncsupport uses unknown stage <" + stage + ">!" ); } } void VulkanHppGenerator::readSyncPipeline( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "depends", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "syncpipelinestage", false } }, {} ); for ( auto const & attribute : attributes ) { if ( attribute.first == "depends" ) { std::vector depends = tokenize( attribute.second, "," ); for ( auto const & d : depends ) { checkForError( isExtension( d ), line, "syncpipeline depends on unknown extension <" + d + ">" ); } } } } void VulkanHppGenerator::readSyncStage( tinyxml2::XMLElement const * element, std::map::const_iterator stageFlagBits2It ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "alias", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "syncequivalent", "syncsupport" } ); std::string alias, name; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else { assert( attribute.first == "name" ); name = attribute.second; } } checkForError( m_syncStages.insert( { name, { alias, line } } ).second, line, "syncstage <" + name + "> already listed" ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "syncequivalent" ) { readSyncStageEquivalent( child, stageFlagBits2It ); } else { assert( value == "syncsupport" ); readSyncStageSupport( child ); } } } void VulkanHppGenerator::readSyncStageEquivalent( tinyxml2::XMLElement const * element, std::map::const_iterator stageFlagBits2It ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "stage", {} } }, {} ); checkElements( line, getChildElements( element ), {}, {} ); for ( auto const & attribute : attributes ) { assert( attribute.first == "stage" ); std::vector stage = tokenize( attribute.second, "," ); for ( auto const & s : stage ) { checkForError( containsName( stageFlagBits2It->second.values, s ), line, "syncequivalent stage uses unknown value <" + s + ">!" ); } } } void VulkanHppGenerator::readSyncStageSupport( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "queues", {} } }, {} ); checkElements( line, getChildElements( element ), {}, {} ); for ( auto const & attribute : attributes ) { assert( attribute.first == "queues" ); checkForError( m_commandQueues.contains( attribute.second ), line, "syncsupport queues uses unknown value <" + attribute.second + ">" ); } } void VulkanHppGenerator::readTag( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "author", {} }, { "contact", {} }, { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string name = attributes.find( "name" )->second; checkForError( m_tags.insert( { name, { line } } ).second, line, "tag <" + name + "> already specified" ); } void VulkanHppGenerator::readTags( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), { { "comment", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, { { "tag", false } } ); for ( auto child : children ) { readTag( child ); } } void VulkanHppGenerator::readTypeBasetype( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); checkAttributes( line, attributes, { { "category", { "basetype" } } }, {} ); NameData nameData; TypeInfo typeInfo; std::tie( nameData, typeInfo ) = readNameAndType( element ); if ( typeInfo.prefix == "typedef" ) { // remove redundant typeInfo.prefix "typedef" typeInfo.prefix.clear(); } checkForError( nameData.arraySizes.empty(), line, "name <" + nameData.name + "> with unsupported arraySizes" ); checkForError( typeInfo.prefix.empty(), line, "unexpected type prefix <" + typeInfo.prefix + ">" ); checkForError( typeInfo.postfix.empty() || ( typeInfo.postfix == "*" ), line, "unexpected type postfix <" + typeInfo.postfix + ">" ); checkForError( m_types.insert( { nameData.name, TypeData{ TypeCategory::BaseType, {}, line } } ).second, line, "basetype <" + nameData.name + "> already specified" ); assert( !m_baseTypes.contains( nameData.name ) ); m_baseTypes[nameData.name] = { typeInfo, line }; } void VulkanHppGenerator::readTypeBitmask( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); if ( attributes.contains( "alias" ) ) { checkAttributes( line, attributes, { { "alias", {} }, { "category", { "bitmask" } }, { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string alias, name; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; } } checkForError( m_types.insert( { name, TypeData{ TypeCategory::Bitmask, {}, line } } ).second, line, "bitmask <" + name + "> already specified" ); auto bitmaskIt = m_bitmasks.find( alias ); checkForError( bitmaskIt != m_bitmasks.end(), line, "bitmask <" + name + "> is an alias of an unknown bitmask <" + alias + ">." ); checkForError( bitmaskIt->second.aliases.insert( { name, line } ).second, line, "bitmask alias <" + name + "> is already listed as an alias for bitmask <" + alias + ">" ); } else { checkAttributes( line, attributes, { { "category", { "bitmask" } } }, { { "api", { "vulkan", "vulkanbase", "vulkansc" } }, { "bitvalues", {} }, { "requires", {} } } ); std::vector api; std::string bitvalues, require; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { api = tokenize( attribute.second, "," ); } else if ( attribute.first == "bitvalues" ) { bitvalues = attribute.second; } else if ( attribute.first == "requires" ) { require = attribute.second; } } NameData nameData; TypeInfo typeInfo; std::tie( nameData, typeInfo ) = readNameAndType( element ); checkForError( nameData.name.starts_with( "Vk" ), line, "name <" + nameData.name + "> does not begin with " ); checkForError( nameData.arraySizes.empty(), line, "name <" + nameData.name + "> with unsupported arraySizes" ); checkForWarning( ( typeInfo.type == "VkFlags" ) || ( typeInfo.type == "VkFlags64" ), line, "unexpected bitmask type <" + typeInfo.type + ">" ); checkForError( typeInfo.prefix == "typedef", line, "unexpected type prefix <" + typeInfo.prefix + ">" ); checkForError( typeInfo.postfix.empty(), line, "unexpected type postfix <" + typeInfo.postfix + ">" ); checkForError( bitvalues.empty() || require.empty(), line, "attributes and are both specified" ); checkForError( ( typeInfo.type != "VkFlags64" ) || !bitvalues.empty(), line, "bitmask of type needs attribute bitvalues to be set" ); if ( !bitvalues.empty() ) { require = bitvalues; } if ( api.empty() || std::ranges::any_of( api, [this]( auto const & a ) { return a == m_api; } ) ) { checkForError( m_types.insert( { nameData.name, TypeData{ TypeCategory::Bitmask, {}, line } } ).second, line, "bitmask <" + nameData.name + "> already specified" ); assert( !m_bitmasks.contains( nameData.name ) ); m_bitmasks[nameData.name] = { {}, require, typeInfo.type, line }; } } } VulkanHppGenerator::DefinesPartition VulkanHppGenerator::partitionDefines( std::map const & defines ) { DefinesPartition partition{}; for ( auto const & define : defines ) { // VK_DEFINE_HANDLE is macro magic that cannot be constexpr-ed // Also filter out the VKSC_ macros, as although they are in the spec, they are not defined in any header. if ( define.first.starts_with( "VK_" ) && ( define.first != "VK_DEFINE_HANDLE" ) ) { if ( define.second.possibleCallee.empty() ) { if ( define.second.possibleDefinition.empty() ) { #if !defined( NDEBUG ) const std::set ignoredDefines{ "VK_DEFINE_NON_DISPATCHABLE_HANDLE", "VK_NULL_HANDLE", "VK_USE_64_BIT_PTR_DEFINES" }; #endif assert( ignoredDefines.contains( define.first ) ); } else { if ( define.second.params.empty() ) { partition.values.insert( define ); } else { partition.callees.insert( define ); } } } else { assert( !define.second.params.empty() && define.second.possibleDefinition.empty() ); partition.callers.insert( define ); } } } return partition; } void VulkanHppGenerator::readTypeDefine( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); checkAttributes( line, attributes, { { "category", { "define" } } }, { { "api", { "vulkan", "vulkanbase", "vulkansc" } }, { "comment", {} }, { "deprecated", { "true" } }, { "name", {} }, { "requires", {} } } ); std::vector api; std::string name, require; bool deprecated = false; for ( auto const & attribute : attributes ) { if ( attribute.first == "api" ) { api = tokenize( attribute.second, "," ); } else if ( attribute.first == "deprecated" ) { deprecated = true; } else if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "requires" ) { require = attribute.second; } } if ( !name.empty() ) { checkForError( !element->FirstChildElement(), line, "unknown formatting of type category=define name <" + name + ">" ); checkForError( element->LastChild() && element->LastChild()->ToText() && element->LastChild()->ToText()->Value(), line, "unknown formatting of type category=define named <" + name + ">" ); } else if ( element->GetText() ) { std::string text = element->GetText(); if ( ( text.find( "class" ) != std::string::npos ) || ( text.find( "struct" ) != std::string::npos ) ) { // here are a couple of structs as defines, which really are types! assert( false ); tinyxml2::XMLElement const * child = element->FirstChildElement(); checkForError( child && ( strcmp( child->Value(), "name" ) == 0 ) && child->GetText(), line, "unexpected formatting of type category=define" ); name = child->GetText(); checkForError( m_types.insert( { name, TypeData{ .category = TypeCategory::Define } } ).second, line, "type <" + name + "> has already been speficied" ); } else { tinyxml2::XMLElement const * child = element->FirstChildElement(); checkForError( child && !child->FirstAttribute() && ( strcmp( child->Value(), "name" ) == 0 ) && child->GetText(), line, "unknown formatting of type category define" ); name = trim( child->GetText() ); if ( ( name == "VK_HEADER_VERSION" ) && ( api.empty() || std::ranges::any_of( api, [this]( auto const & a ) { return a == m_api; } ) ) ) { m_version = trimEnd( element->LastChild()->ToText()->Value() ); } // ignore all the other defines checkForWarning( !child->NextSiblingElement() || ( child->NextSiblingElement() && !child->NextSiblingElement()->FirstAttribute() && ( strcmp( child->NextSiblingElement()->Value(), "type" ) == 0 ) && !child->NextSiblingElement()->NextSiblingElement() ), line, "unknown formatting of type category define" ); } } assert( !name.empty() ); if ( api.empty() || std::ranges::any_of( api, [this]( auto const & a ) { return a == m_api; } ) ) { MacroVisitor definesVisitor{}; element->Accept( &definesVisitor ); auto const & [deprecatedReason, possibleCallee, params, possibleDefinition] = parseMacro( definesVisitor.macro ); checkForError( m_types.insert( { name, TypeData{ TypeCategory::Define, {}, line } } ).second, line, "define <" + name + "> already specified" ); assert( !m_defines.contains( name ) ); m_defines[name] = { deprecated, require, line, deprecatedReason, possibleCallee, params, possibleDefinition }; } } void VulkanHppGenerator::readTypeEnum( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); checkAttributes( line, attributes, { { "category", { "enum" } }, { "name", {} } }, { { "alias", {} } } ); checkElements( line, getChildElements( element ), {} ); std::string alias, name; for ( auto const & attribute : attributes ) { if ( attribute.first == "alias" ) { alias = attribute.second; } else if ( attribute.first == "name" ) { name = attribute.second; } } checkForError( m_types.insert( { name, TypeData{ TypeCategory::Enum, {}, line } } ).second, line, "enum <" + name + "> already specified" ); if ( alias.empty() ) { assert( !m_enums.contains( name ) ); m_enums[name] = EnumData{ .xmlLine = line }; } else { auto enumIt = m_enums.find( alias ); checkForError( enumIt != m_enums.end(), line, "enum <" + name + "> is an alias of an unknown enum <" + alias + ">" ); checkForError( enumIt->second.aliases.insert( { name, line } ).second, line, "enum <" + name + "> already specified as some alias" ); } } void VulkanHppGenerator::readTypeFuncpointer( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); checkAttributes( line, attributes, { { "category", { "funcpointer" } } }, { { "requires", {} } } ); std::string require; for ( auto const & attribute : attributes ) { if ( attribute.first == "requires" ) { require = attribute.second; } } std::vector children = getChildElements( element ); std::string name; std::vector params; TypeInfo returnType; auto nameIt = std::ranges::find_if( children, []( tinyxml2::XMLElement const * e ) { return std::string( e->Value() ) == "name"; } ); if ( nameIt != children.end() ) { // found the old fashioned funcpointer specification via "name" and "type" checkElements( line, children, { { "name", true } }, { "type" } ); std::string text = element->GetText(); assert( text.starts_with( "typedef " ) && text.ends_with( " (VKAPI_PTR *" ) ); text = stripPostfix( stripPrefix( text, "typedef " ), " (VKAPI_PTR *" ); if ( text.ends_with( "*" ) ) { returnType.postfix = "*"; text = stripPostfix( text, "*" ); } returnType.type = text; for ( auto const & child : children ) { std::string value = child->Value(); if ( value == "name" ) { name = child->GetText(); } else if ( value == "type" ) { const int paramLine = child->GetLineNum(); TypeInfo typeInfo; typeInfo.type = child->GetText(); auto previousSibling = child->PreviousSibling(); assert( previousSibling ); char const * previousSiblingValue = previousSibling->Value(); assert( previousSiblingValue ); std::string previousSiblingText = previousSiblingValue; if ( previousSiblingText.ends_with( "const " ) ) { typeInfo.prefix = "const"; } auto nextSibling = child->NextSibling(); assert( nextSibling ); char const * nextSiblingValue = nextSibling->Value(); assert( nextSiblingValue != nullptr ); std::string paramName = nextSiblingValue; if ( paramName.front() == '*' ) { typeInfo.postfix = "*"; } paramName = paramName.substr( paramName.find_first_not_of( "* " ) ); paramName = paramName.substr( 0, paramName.find_first_of( ",)" ) ); checkForError( std::ranges::none_of( params, [¶mName]( ParamData const & param ) { return param.name == paramName; } ), line, "param <" + paramName + "> already listed in funcpointer <" + name + ">" ); params.push_back( { paramName, typeInfo, paramLine } ); } } } else { // found the new funcpointer specification via "proto" and "param" checkElements( line, children, { { "proto", true } }, { "param" } ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "param" ) { std::pair result = readCommandParam( child, params ); assert( result.first ); params.push_back( result.second ); } else if ( value == "proto" ) { std::tie( name, returnType ) = readCommandProto( child, "PFN_vk" ); } } } assert( !name.empty() ); checkForError( m_types.insert( { name, TypeData{ TypeCategory::FuncPointer, {}, line } } ).second, line, "funcpointer <" + name + "> already specified" ); assert( !m_funcPointers.contains( name ) ); m_funcPointers[name] = { params, require, returnType, line }; } void VulkanHppGenerator::readTypeHandle( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); auto aliasIt = attributes.find( "alias" ); if ( aliasIt != attributes.end() ) { checkAttributes( line, attributes, { { "alias", {} }, { "category", { "handle" } }, { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string alias = aliasIt->second; std::string name = attributes.find( "name" )->second; checkForError( m_types.insert( { name, TypeData{ TypeCategory::Handle, {}, line } } ).second, line, "handle <" + name + "> already specified" ); auto handleIt = m_handles.find( alias ); checkForError( handleIt != m_handles.end(), line, "handle <" + name + "> uses unknown alias <" + alias + ">." ); checkForError( handleIt->second.aliases.insert( { name, line } ).second, line, "handle <" + name + "> already listed as alias for handle <" + alias + ">" ); } else { checkAttributes( line, attributes, { { "category", { "handle" } }, { "objtypeenum", {} } }, { { "parent", {} } } ); HandleData handleData; handleData.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "objtypeenum" ) { handleData.objTypeEnum = attribute.second; } else if ( attribute.first == "parent" ) { handleData.parent = attribute.second; } } NameData nameData; TypeInfo typeInfo; std::tie( nameData, typeInfo ) = readNameAndType( element ); handleData.isDispatchable = typeInfo.type == "VK_DEFINE_HANDLE"; checkForError( nameData.name.starts_with( "Vk" ), line, "name <" + nameData.name + "> does not begin with " ); checkForError( nameData.arraySizes.empty(), line, "name <" + nameData.name + "> with unsupported arraySizes" ); checkForError( ( typeInfo.type == "VK_DEFINE_HANDLE" ) || ( typeInfo.type == "VK_DEFINE_NON_DISPATCHABLE_HANDLE" ), line, "handle with invalid type <" + typeInfo.type + ">" ); checkForError( typeInfo.prefix.empty(), line, "unexpected type prefix <" + typeInfo.prefix + ">" ); checkForError( typeInfo.postfix == "(", line, "unexpected type postfix <" + typeInfo.postfix + ">" ); checkForError( !handleData.objTypeEnum.empty(), line, "handle <" + nameData.name + "> does not specify attribute \"objtypeenum\"" ); checkForError( m_types.insert( { nameData.name, TypeData{ TypeCategory::Handle, {}, line } } ).second, line, "handle <" + nameData.name + "> already specified" ); assert( !m_handles.contains( nameData.name ) ); m_handles.insert( { nameData.name, handleData } ); } } void VulkanHppGenerator::readTypeInclude( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); checkAttributes( line, attributes, { { "category", { "include" } }, { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string name = attributes.find( "name" )->second; checkForError( m_types.insert( { name, TypeData{ TypeCategory::Include, {}, line } } ).second, line, "type <" + name + "> already specified" ); assert( !m_includes.contains( name ) ); m_includes[name] = { line }; } void VulkanHppGenerator::readTypeRequires( tinyxml2::XMLElement const * element, std::map const & attributes ) { const int line = element->GetLineNum(); checkAttributes( line, attributes, { { "name", {} }, { "requires", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string name, require; for ( auto attribute : attributes ) { if ( attribute.first == "name" ) { name = attribute.second; } else { assert( attribute.first == "requires" ); require = attribute.second; } } checkForError( m_includes.contains( require ), line, "type <" + name + "> requires unknown <" + require + ">" ); checkForError( m_types.insert( { name, TypeData{ TypeCategory::ExternalType, {}, line } } ).second, line, "type <" + name + "> already specified" ); assert( !m_externalTypes.contains( name ) ); m_externalTypes[name] = { require, line }; } void VulkanHppGenerator::readTypeStruct( tinyxml2::XMLElement const * element, bool isUnion, std::map const & attributes ) { const int line = element->GetLineNum(); auto aliasIt = attributes.find( "alias" ); if ( aliasIt != attributes.end() ) { checkAttributes( line, attributes, { { "alias", {} }, { "category", { "struct" } }, { "name", {} } }, {} ); checkElements( line, getChildElements( element ), {}, {} ); std::string alias = aliasIt->second; std::string name = attributes.find( "name" )->second; checkForError( m_types.insert( { name, TypeData{ TypeCategory::Struct, {}, line } } ).second, line, "struct <" + name + "> already specified" ); checkForError( std::ranges::none_of( m_structsAliases, [&name]( std::pair const & ad ) { return ad.first == name; } ), line, "struct alias <" + name + "> already listed" ); m_structsAliases.push_back( { name, { alias, line } } ); } else { checkAttributes( line, attributes, { { "category", { isUnion ? "union" : "struct" } }, { "name", {} } }, { { "allowduplicate", { "false", "true" } }, { "comment", {} }, { "requiredlimittype", { "true" } }, { "returnedonly", { "true" } }, { "structextends", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "member", "comment" } ); StructData structureData; structureData.xmlLine = line; structureData.isUnion = isUnion; std::string name; for ( auto const & attribute : attributes ) { if ( attribute.first == "allowduplicate" ) { structureData.allowDuplicate = ( attribute.second == "true" ); } else if ( attribute.first == "category" ) { assert( isUnion ? ( attribute.second == "union" ) : ( attribute.second == "struct" ) ); } else if ( attribute.first == "name" ) { name = attribute.second; } else if ( attribute.first == "requiredlimittype" ) { assert( attribute.second == "true" ); structureData.requiredLimitType = true; } else if ( attribute.first == "returnedonly" ) { assert( attribute.second == "true" ); structureData.returnedOnly = true; } else if ( attribute.first == "structextends" ) { structureData.structExtends = tokenize( attribute.second, "," ); } } assert( !name.empty() ); checkForError( !structureData.allowDuplicate || !structureData.structExtends.empty(), line, "attribute is true, but no structures are listed in " ); checkForError( m_types.insert( { name, TypeData{ isUnion ? TypeCategory::Union : TypeCategory::Struct, {}, line } } ).second, line, "struct <" + name + "> already specified" ); assert( !m_structs.contains( name ) ); std::map::iterator it = m_structs.insert( { name, structureData } ).first; for ( auto child : children ) { std::string value = child->Value(); if ( value == "member" ) { readStructMember( child, it->second.members, isUnion ); } } // check for any "len" references needs to be done after all members are read, as the len might name a member that's listed after the array member for ( auto & member : it->second.members ) { if ( !member.lenExpressions.empty() && ( member.lenExpressions[0] != "null-terminated" ) && member.lenMembers.empty() ) { auto lenMemberIt = findByName( it->second.members, member.lenExpressions[0] ); checkForError( lenMemberIt != it->second.members.end(), line, "member attribute holds unknown value <" + member.lenExpressions[0] + ">" ); member.lenMembers.push_back( { member.lenExpressions[0], std::distance( it->second.members.begin(), lenMemberIt ) } ); } } checkForError( !structureData.requiredLimitType || std::ranges::all_of( structureData.members, []( MemberData const & member ) { return ( member.name == "sType" ) || ( member.name == "pNext" ) || !member.limitType.empty(); } ), line, "struct <" + name + "> requires all members to have a limittype" ); it->second.subStruct = determineSubStruct( *it ); // add some default values for some structures here! if ( ( it->first == "VkRayTracingShaderGroupCreateInfoNV" ) || ( it->first == "VkRayTracingShaderGroupCreateInfoKHR" ) ) { assert( ( ( it->first != "VkRayTracingShaderGroupCreateInfoNV" ) || ( it->second.members.size() == 7 ) ) && ( ( it->first != "VkRayTracingShaderGroupCreateInfoKHR" ) || ( it->second.members.size() == 8 ) ) ); assert( ( it->second.members[3].name == "generalShader" ) && ( it->second.members[4].name == "closestHitShader" ) && ( it->second.members[5].name == "anyHitShader" ) && ( it->second.members[6].name == "intersectionShader" ) ); const bool isKHR = ( it->first == "VkRayTracingShaderGroupCreateInfoKHR" ); for ( const size_t i : { 3, 4, 5, 6 } ) { it->second.members[i].defaultValue = isKHR ? "VK_SHADER_UNUSED_KHR" : "VK_SHADER_UNUSED_NV"; } } // check if multiple structure members use the very same (not empty) len attribute // Note: even though the arrays are not marked as optional, they still might be mutually exclusive (like in // VkWriteDescriptorSet)! That is, there's not enough information available in vk.xml to decide on that, so we // need this external knowledge! static std::set mutualExclusiveStructs = { "VkAccelerationStructureBuildGeometryInfoKHR", "VkAccelerationStructureTrianglesOpacityMicromapEXT", "VkMicromapBuildInfoEXT", "VkWriteDescriptorSet" }; static std::set multipleLenStructs = { "VkAccelerationStructureTrianglesDisplacementMicromapNV", "VkImageConstraintsInfoFUCHSIA", "VkIndirectCommandsLayoutTokenNV", "VkIndirectExecutionSetShaderInfoEXT", "VkPipelineBinaryKeysAndDataKHR", "VkPresentInfoKHR", "VkSemaphoreWaitInfo", "VkSetDescriptorBufferOffsetsInfoEXT", "VkSubmitInfo", "VkSubpassDescription", "VkSubpassDescription2", "VkSwapchainTimeDomainPropertiesEXT", "VkTensorCopyARM", "VkTensorDescriptionARM", "VkVideoDecodeAV1PictureInfoKHR", "VkWin32KeyedMutexAcquireReleaseInfoKHR", "VkWin32KeyedMutexAcquireReleaseInfoNV" }; bool warned = false; for ( auto m0It = it->second.members.begin(); !warned && ( m0It != it->second.members.end() ); ++m0It ) { if ( !m0It->lenExpressions.empty() && ( m0It->lenExpressions.front() != "null-terminated" ) ) { for ( auto m1It = std::next( m0It ); !warned && ( m1It != it->second.members.end() ); ++m1It ) { if ( !m1It->lenExpressions.empty() && ( m0It->lenExpressions.front() == m1It->lenExpressions.front() ) ) { if ( mutualExclusiveStructs.contains( it->first ) ) { it->second.mutualExclusiveLens = true; } else { checkForWarning( multipleLenStructs.contains( it->first ), line, "Encountered structure <" + it->first + "> with multiple members referencing the same member for len. Need to be checked if they are supposed to be mutually exclusive." ); warned = true; } } } } } } } void VulkanHppGenerator::readTypes( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, { { "comment", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "type", false } }, { "comment" } ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "type" ) { readTypesType( child ); } } } void VulkanHppGenerator::readTypesType( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); auto categoryIt = attributes.find( "category" ); if ( categoryIt != attributes.end() ) { if ( categoryIt->second == "basetype" ) { readTypeBasetype( element, attributes ); } else if ( categoryIt->second == "bitmask" ) { readTypeBitmask( element, attributes ); } else if ( categoryIt->second == "define" ) { readTypeDefine( element, attributes ); } else if ( categoryIt->second == "enum" ) { readTypeEnum( element, attributes ); } else if ( categoryIt->second == "funcpointer" ) { readTypeFuncpointer( element, attributes ); } else if ( categoryIt->second == "handle" ) { readTypeHandle( element, attributes ); } else if ( categoryIt->second == "include" ) { readTypeInclude( element, attributes ); } else if ( ( categoryIt->second == "struct" ) || ( categoryIt->second == "union" ) ) { readTypeStruct( element, ( categoryIt->second == "union" ), attributes ); } else { checkForError( false, line, "unknown category <" + categoryIt->second + "> encountered" ); } } else { auto requiresIt = attributes.find( "requires" ); if ( requiresIt != attributes.end() ) { readTypeRequires( element, attributes ); } else { checkForError( ( attributes.size() == 1 ) && ( attributes.begin()->first == "name" ) && ( attributes.begin()->second == "int" ), line, "unknown type" ); checkForError( m_types.insert( { "int", TypeData{ TypeCategory::Unknown, {}, line } } ).second, line, "type already specified" ); } } } TypeInfo VulkanHppGenerator::readTypeInfo( tinyxml2::XMLElement const * element ) const { TypeInfo typeInfo; tinyxml2::XMLNode const * previousSibling = element->PreviousSibling(); if ( previousSibling && previousSibling->ToText() ) { typeInfo.prefix = trim( previousSibling->Value() ); } typeInfo.type = element->GetText(); tinyxml2::XMLNode const * nextSibling = element->NextSibling(); if ( nextSibling && nextSibling->ToText() ) { typeInfo.postfix = trimStars( trimEnd( nextSibling->Value() ) ); } return typeInfo; } void VulkanHppGenerator::readVideoCapabilities( tinyxml2::XMLElement const * element, VideoCodec & videoCodec ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "struct", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string capabilities = attributes.find( "struct" )->second; checkForError( std::ranges::find( videoCodec.capabilities, capabilities ) == videoCodec.capabilities.end(), line, "videocapabilities struct <" + capabilities + "> already listed for videoCodec <" + videoCodec.name + ">" ); checkForError( m_structs.contains( capabilities ), line, "videocodec <" + videoCodec.name + "> lists unknown capabilities struct <" + capabilities + ">" ); videoCodec.capabilities.push_back( capabilities ); } void VulkanHppGenerator::readVideoCodec( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, { { "extend", {} }, { "value", {} } } ); std::vector children = getChildElements( element ); checkElements( line, children, { { "videocapabilities", false } }, { "videoformat", "videoprofiles" } ); VideoCodec videoCodec; videoCodec.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "extend" ) { videoCodec.extend = attribute.second; } else if ( attribute.first == "name" ) { videoCodec.name = attribute.second; } else if ( attribute.first == "value" ) { videoCodec.value = attribute.second; } } checkForError( findByName( m_videoCodecs, videoCodec.name ) == m_videoCodecs.end(), line, "Video Codec <" + videoCodec.name + "> already listed" ); checkForError( videoCodec.extend.empty() || findByName( m_videoCodecs, videoCodec.extend ) != m_videoCodecs.end(), line, "Video Codec <" + videoCodec.name + "> extends unknown <" + videoCodec.extend + ">" ); if ( !videoCodec.value.empty() ) { auto enumIt = m_enums.find( "VkVideoCodecOperationFlagBitsKHR" ); assert( enumIt != m_enums.end() ); checkForError( findByName( enumIt->second.values, videoCodec.value ) != enumIt->second.values.end(), line, "Video Codec <" + videoCodec.name + "> lists unknown value <" + videoCodec.value + ">" ); } for ( auto child : children ) { std::string value = child->Value(); if ( value == "videocapabilities" ) { readVideoCapabilities( child, videoCodec ); } else if ( value == "videoformat" ) { readVideoFormat( child, videoCodec ); } else if ( value == "videoprofiles" ) { readVideoProfiles( child, videoCodec ); } } m_videoCodecs.push_back( videoCodec ); } void VulkanHppGenerator::readVideoCodecs( tinyxml2::XMLElement const * element ) { const int line = element->GetLineNum(); checkAttributes( line, getAttributes( element ), {}, {} ); std::vector children = getChildElements( element ); checkElements( line, children, { { "videocodec", false } } ); for ( auto child : children ) { std::string value = child->Value(); assert( value == "videocodec" ); readVideoCodec( child ); } } void VulkanHppGenerator::readVideoFormat( tinyxml2::XMLElement const * element, VideoCodec & videoCodec ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); if ( attributes.contains( "extend" ) ) { checkAttributes( line, attributes, { { "extend", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, { { "videoformatproperties", true } } ); std::string extend = attributes.find( "extend" )->second; std::vector::iterator videoFormatIt; auto videoCodecIt = std::ranges::find_if( m_videoCodecs, [&extend, &videoFormatIt]( auto & vc ) { videoFormatIt = std::ranges::find_if( vc.formats, [&extend]( auto & vf ) { return vf.name == extend; } ); return videoFormatIt != vc.formats.end(); } ); checkForError( videoCodecIt != m_videoCodecs.end(), line, "videocodec <" + videoCodec.name + "> extends unknown videoformat <" + extend + ">" ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "videoformatproperties" ) { readVideoFormatProperties( child, videoCodecIt->name, *videoFormatIt ); } } } else { checkAttributes( line, attributes, { { "name", {} }, { "usage", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "videoformatproperties", "videorequirecapabilities" } ); auto flagBitsIt = m_enums.find( "VkImageUsageFlagBits" ); assert( flagBitsIt != m_enums.end() ); VideoFormat format; format.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "name" ) { format.name = attribute.second; checkForError( findByName( videoCodec.formats, format.name ) == videoCodec.formats.end(), line, "Video Format <" + format.name + "> has already been listed for Video Codec <" + videoCodec.name + ">" ); } else if ( attribute.first == "usage" ) { format.usage = tokenize( attribute.second, "+" ); for ( auto const & usage : format.usage ) { checkForError( findByName( flagBitsIt->second.values, usage ) != flagBitsIt->second.values.end(), line, "Unknown Video Format <" + format.name + "> listed for Video Codec <" + videoCodec.name + ">" ); } } } videoCodec.formats.push_back( format ); for ( auto child : children ) { std::string value = child->Value(); if ( value == "videoformatproperties" ) { readVideoFormatProperties( child, videoCodec.name, videoCodec.formats.back() ); } else if ( value == "videorequirecapabilities" ) { readVideoRequireCapabilities( child, videoCodec ); } } } } void VulkanHppGenerator::readVideoProfileMember( tinyxml2::XMLElement const * element, VideoCodec & videoCodec ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "videoprofile" } ); VideoProfileMember profileMember; profileMember.xmlLine = line; profileMember.name = attributes.find( "name" )->second; checkForError( findByName( videoCodec.profiles.back().members, profileMember.name ) == videoCodec.profiles.back().members.end(), line, "Video Profile Member <" + profileMember.name + "> already listed for Video Profiles <" + videoCodec.profiles.back().name ); videoCodec.profiles.back().members.push_back( profileMember ); for ( auto child : children ) { std::string value = child->Value(); assert( value == "videoprofile" ); readVideoProfile( child, videoCodec ); } } void VulkanHppGenerator::readVideoProfile( tinyxml2::XMLElement const * element, VideoCodec & videoCodec ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "name", {} }, { "value", {} } }, {} ); checkElements( line, getChildElements( element ), {}, {} ); VideoProfile profile; profile.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "name" ) { profile.name = attribute.second; } else if ( attribute.first == "value" ) { profile.value = attribute.second; } } checkForError( findByName( videoCodec.profiles.back().members.back().profiles, profile.name ) == videoCodec.profiles.back().members.back().profiles.end(), line, "Video Profile <" + profile.name + "> already listed for VideoProfileMember <" + videoCodec.profiles.back().members.back().name ); // value checking would need cross-loading of video.xml !! videoCodec.profiles.back().members.back().profiles.push_back( profile ); } void VulkanHppGenerator::readVideoProfiles( tinyxml2::XMLElement const * element, VideoCodec & videoCodec ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "struct", {} } }, {} ); std::vector children = getChildElements( element ); checkElements( line, children, {}, { "videoprofilemember" } ); VideoProfiles profiles; profiles.xmlLine = line; profiles.name = attributes.find( "struct" )->second; checkForError( findByName( videoCodec.profiles, profiles.name ) == videoCodec.profiles.end(), line, "Video Profiles struct <" + profiles.name + "> already listed for Video Codec <" + videoCodec.name + ">" ); checkForError( m_structs.find( profiles.name ) != m_structs.end(), line, "Unknown Video Profiles struct <" + profiles.name + "> used with VideoCodec <" + videoCodec.name + ">" ); videoCodec.profiles.push_back( profiles ); for ( auto child : children ) { std::string value = child->Value(); assert( value == "videoprofilemember" ); readVideoProfileMember( child, videoCodec ); } } void VulkanHppGenerator::readVideoFormatProperties( tinyxml2::XMLElement const * element, std::string const & videoCodec, VideoFormat & videoFormat ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "struct", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); std::string formatProperties = attributes.find( "struct" )->second; checkForError( m_structs.find( formatProperties ) != m_structs.end(), line, "videoCodec <" + videoCodec + "> lists unknown struct <" + formatProperties + "> as videoformatproperties" ); checkForError( std::ranges::find( videoFormat.formatProperties, formatProperties ) == videoFormat.formatProperties.end(), line, "videoformatproperties <" + formatProperties + "> listed in videocodec <" + videoCodec + "> already listed for videoformat <" + videoFormat.name + ">" ); videoFormat.formatProperties.push_back( formatProperties ); } void VulkanHppGenerator::readVideoRequireCapabilities( tinyxml2::XMLElement const * element, VideoCodec & videoCodec ) { const int line = element->GetLineNum(); std::map attributes = getAttributes( element ); checkAttributes( line, attributes, { { "member", {} }, { "struct", {} }, { "value", {} } }, {} ); checkElements( line, getChildElements( element ), {} ); VideoRequireCapabilities requireCapabilities; requireCapabilities.xmlLine = line; for ( auto const & attribute : attributes ) { if ( attribute.first == "member" ) { requireCapabilities.member = attribute.second; } else if ( attribute.first == "struct" ) { requireCapabilities.name = attribute.second; } else if ( attribute.first == "value" ) { requireCapabilities.value = attribute.second; } } VideoFormat & format = videoCodec.formats.back(); auto structIt = m_structs.find( requireCapabilities.name ); checkForError( structIt != m_structs.end(), line, "Unknown Video Require Capablities struct <" + requireCapabilities.name + "> listed for Video Format <" + format.name + "> in VideoCodec <" + videoCodec.name + ">" ); auto memberIt = findByName( structIt->second.members, requireCapabilities.member ); checkForError( memberIt != structIt->second.members.end(), line, "Unknown member <" + requireCapabilities.member + "> used for Video Require Capabilities struct <" + requireCapabilities.name + "> in VideoFormat <" + format.name + "> in VideoCodec <" + videoCodec.name + ">" ); auto bitmaskIt = m_bitmasks.find( memberIt->type.type ); checkForError( bitmaskIt != m_bitmasks.end(), line, "Video Require Capabilities member <" + requireCapabilities.member + "> is not a bitmask" ); auto enumIt = m_enums.find( bitmaskIt->second.require ); assert( enumIt != m_enums.end() ); checkForError( findByName( enumIt->second.values, requireCapabilities.value ) != enumIt->second.values.end(), line, "Unknown value <" + requireCapabilities.value + "> listed for Video Require Capabilities <" + requireCapabilities.name + "> in Video Codec <" + videoCodec.name + ">" ); format.requireCapabilities.push_back( requireCapabilities ); } void VulkanHppGenerator::registerDeleter( std::string const & commandName, CommandData const & commandData ) { // some special handling for release functions that don't release an object const std::set noDeleterFunctions = { "vkReleaseCapturedPipelineData", "vkReleaseFullScreenExclusiveMode", "vkReleaseProfilingLock", "vkReleaseSwapchainImages" }; std::string tag = findTag( commandName ); if ( ( commandName.substr( 2, 7 ) == "Destroy" ) || ( commandName.substr( 2, 4 ) == "Free" ) || ( ( commandName.substr( 2, 7 ) == "Release" ) && !noDeleterFunctions.contains( stripPostfix( commandName, tag ) ) ) ) { std::string key; size_t valueIndex = 0; switch ( commandData.params.size() ) { case 2: if ( commandData.params.back().type.type == "VkAllocationCallbacks" ) { key = ""; valueIndex = 0; } else { key = commandData.params[0].type.type; valueIndex = 1; } break; case 3: assert( commandData.params.back().type.type == "VkAllocationCallbacks" ); key = commandData.params[0].type.type; valueIndex = 1; break; case 4: key = commandData.params[0].type.type; valueIndex = 3; assert( m_handles.contains( commandData.params[valueIndex].type.type ) ); m_handles.find( commandData.params[valueIndex].type.type )->second.deletePool = commandData.params[1].type.type; break; default: assert( false ); } auto keyHandleIt = m_handles.find( key ); assert( keyHandleIt != m_handles.end() ); keyHandleIt->second.childrenHandles.insert( commandData.params[valueIndex].type.type ); auto handleIt = m_handles.find( commandData.params[valueIndex].type.type ); assert( handleIt != m_handles.end() ); handleIt->second.deleteCommand = commandName; handleIt->second.destructorType = key; } } void VulkanHppGenerator::rescheduleRAIIHandle( std::string & str, std::pair const & handle, std::set & listedHandles, std::set const & specialFunctions ) const { listedHandles.insert( handle.first ); if ( !handle.second.parent.empty() && !listedHandles.contains( handle.second.parent ) ) { auto parentIt = m_handles.find( handle.second.parent ); assert( parentIt != m_handles.end() ); str += generateRAIIHandle( *parentIt, listedHandles, specialFunctions ); } for ( auto constructorIt : handle.second.constructorIts ) { for ( auto const & param : constructorIt->second.params ) { auto handleIt = m_handles.find( param.type.type ); if ( handleIt != m_handles.end() && !listedHandles.contains( param.type.type ) ) { str += generateRAIIHandle( *handleIt, listedHandles, specialFunctions ); } } } } std::vector VulkanHppGenerator::selectCommandsByHandle( std::vector const & requireData, std::set const & handleCommands, std::set & listedCommands ) const { std::vector selectedCommands; forEachRequiredCommand( requireData, [&]( NameLine const & command, auto const & ) { if ( handleCommands.contains( command.name ) && listedCommands.insert( command.name ).second ) { selectedCommands.push_back( command.name ); } } ); return selectedCommands; } bool VulkanHppGenerator::skipLeadingGrandParent( std::pair const & handle ) const { bool skip = false; assert( !handle.second.constructorIts.empty() ); auto constructorIt = handle.second.constructorIts.begin(); if ( ( 1 < ( *constructorIt )->second.params.size() ) && isHandleType( ( *constructorIt )->second.params[0].type.type ) && ( ( *constructorIt )->second.params[1].type.type == handle.second.parent ) ) { auto parentIt = m_handles.find( handle.second.parent ); assert( parentIt != m_handles.end() ); skip = ( ( *constructorIt )->second.params[0].type.type == parentIt->second.parent ); #if !defined( NDEBUG ) for ( auto it = std::next( constructorIt ); it != handle.second.constructorIts.end(); ++it ) { assert( ( *it )->second.params[0].type.type == ( *constructorIt )->second.params[0].type.type ); assert( !skip || ( ( *it )->second.params[1].type.type == ( *constructorIt )->second.params[1].type.type ) ); } #endif } return skip; } std::string VulkanHppGenerator::stripPluralS( std::string const & name ) const { std::string strippedName = name; std::string tag = findTag( name ); if ( strippedName.ends_with( "s" + tag ) ) { const size_t pos = strippedName.rfind( 's' ); if ( ( 2 <= pos ) && ( strippedName.substr( pos - 2, 3 ) == "ies" ) ) { strippedName.replace( pos - 2, 3, "y" ); } else { strippedName.erase( pos, 1 ); } } return strippedName; } bool VulkanHppGenerator::structureChainHoldsHandle( std::string const & name ) const { auto structIt = findByNameOrAlias( m_structs, name ); assert( ( structIt != m_structs.end() ) && !structIt->second.extendedBy.empty() ); auto extendedByIt = std::ranges::find_if( structIt->second.extendedBy, [this]( auto const & s ) { auto sIt = m_structs.find( s ); assert( sIt != m_structs.end() ); return structureHoldsHandle( sIt->second ); } ); return extendedByIt != structIt->second.extendedBy.end(); } bool VulkanHppGenerator::structureChainHoldsVector( std::string const & name ) const { auto structIt = findByNameOrAlias( m_structs, name ); assert( ( structIt != m_structs.end() ) && !structIt->second.extendedBy.empty() ); auto extendedByIt = std::ranges::find_if( structIt->second.extendedBy, [this]( auto const & s ) { auto sIt = m_structs.find( s ); assert( sIt != m_structs.end() ); return structureHoldsVector( sIt->second ); } ); return extendedByIt != structIt->second.extendedBy.end(); } bool VulkanHppGenerator::structureHoldsHandle( StructData const & structData ) const { return findHandleMember( structData.members ) != structData.members.end(); } bool VulkanHppGenerator::structureHoldsVector( StructData const & structData ) const { return findVectorMember( structData.members ) != structData.members.end(); } std::string VulkanHppGenerator::toString( TypeCategory category ) { switch ( category ) { case TypeCategory::Bitmask : return "bitmask"; case TypeCategory::BaseType : return "basetype"; case TypeCategory::Define : return "define"; case TypeCategory::Enum : return "enum"; case TypeCategory::ExternalType: return "ExternalType"; case TypeCategory::FuncPointer : return "funcpointer"; case TypeCategory::Handle : return "handle"; case TypeCategory::Struct : return "struct"; case TypeCategory::Union : return "union"; case TypeCategory::Unknown : return "unkown"; default : assert( false ); return ""; } } VulkanHppGenerator::MemberData const & VulkanHppGenerator::vectorMemberByStructure( std::string const & structureType ) const { auto structIt = m_structs.find( structureType ); assert( structIt != m_structs.end() ); assert( describesVector( structIt->second ) ); return structIt->second.members.back(); } bool VulkanHppGenerator::EnumData::addEnumAlias( int line, std::string const & name, std::string const & alias, std::string const & protect, bool supported ) { auto aliasIt = findByName( valueAliases, name ); bool ok = ( aliasIt == valueAliases.end() ); if ( ok ) { valueAliases.push_back( { alias, name, protect, supported, line } ); } else { ok = ( ( aliasIt->alias == alias ) && ( aliasIt->protect == protect ) && ( aliasIt->supported == supported ) ); } return ok; } bool VulkanHppGenerator::EnumData::addEnumValue( int line, std::string const & name, std::string const & protect, std::string const & bitpos, std::string const & value, bool supported, bool deprecated ) { auto valueIt = findByName( values, name ); bool ok = ( valueIt == values.end() ); if ( ok ) { values.push_back( { {}, bitpos, deprecated, name, protect, supported, value, line } ); } else if ( supported ) // only for supported enum values, we need to check for consistency! { ok = ( bitpos == valueIt->bitpos ) && ( protect == valueIt->protect ) && ( value == valueIt->value ); // if a previous version was not supported, make it supported now if ( !valueIt->supported ) { valueIt->supported = true; valueIt->xmlLine = line; } } return ok || !supported; } // // VulkanHppGenerator local functions // namespace { template bool containsByName( std::vector const & values, std::string const & name ) { return std::ranges::any_of( values, [&name]( T const & value ) { return value.name == name; } ); } template bool containsByNameOrAlias( std::map const & values, std::string const & name ) { return std::ranges::any_of( values, [&name]( std::pair const & value ) { return value.first == name || value.second.aliases.contains( name ); } ); } std::vector> filterNumbers( std::vector const & names ) { std::vector> filteredNames; for ( auto const & name : names ) { if ( !isNumber( name ) ) { filteredNames.push_back( { name, ~0 } ); } } return filteredNames; } template typename std::vector::const_iterator findByName( std::vector const & values, std::string const & name ) { return std::ranges::find_if( values, [&name]( T const & value ) { return value.name == name; } ); } template typename std::vector::iterator findByName( std::vector & values, std::string const & name ) { return std::ranges::find_if( values, [&name]( T const & value ) { return value.name == name; } ); } template typename std::map::const_iterator findByNameOrAlias( std::map const & values, std::string const & name ) { auto it = values.find( name ); if ( it == values.end() ) { it = std::ranges::find_if( values, [&name]( auto const & value ) { return ( value.first == name ) || value.second.aliases.contains( name ); } ); } return it; } template typename std::map::iterator findByNameOrAlias( std::map & values, std::string const & name ) { auto it = values.find( name ); if ( it == values.end() ) { it = std::ranges::find_if( values, [&name]( auto const & value ) { return ( value.first == name ) || value.second.aliases.contains( name ); } ); } assert( it != values.end() ); return it; } template typename std::vector::const_iterator findByNameOrAlias( std::vector const & values, std::string const & name ) { return std::ranges::find_if( values, [&name]( T const & value ) { return ( value.name == name ) || std::ranges::any_of( value.aliases, [&name]( auto const & eav ) { return eav.name == name; } ); } ); } template typename std::vector::const_iterator findByType( std::vector const & values, std::string const & type ) { return std::ranges::find_if( values, [&type]( T const & value ) { return value.type.type == type; } ); } std::string generateCArraySizes( std::vector const & sizes ) { std::string arraySizes; for ( auto const & s : sizes ) { arraySizes += "[" + s + "]"; } return arraySizes; } std::string generateList( std::vector const & elements, std::string const & prefix, std::string const & separator ) { std::string list; if ( !elements.empty() ) { list = prefix + elements[0]; for ( auto it = std::next( elements.begin() ); it != elements.end(); ++it ) { list += separator + *it; } } return list; } std::string generateNoDiscard( bool returnsSomething, bool multiSuccessCodes, bool multiErrorCodes ) { return ( returnsSomething || multiSuccessCodes ) ? "VULKAN_HPP_NODISCARD " : ( multiErrorCodes ? "VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS " : "" ); } std::string generateStandardArray( std::string const & type, std::vector const & sizes ) { std::string arrayString = "std::array<" + type + "," + sizes.back() + ">"; for ( size_t i = sizes.size() - 2; i < sizes.size(); i-- ) { arrayString = "std::array<" + arrayString + "," + sizes[i] + ">"; } return arrayString; } bool isUpperCase( std::string const & name ) { return std::ranges::all_of( name, []( auto const & c ) noexcept { return c == toupper( c ); } ); } // function to take three or four-vector of strings containing a macro definition, and return // a tuple with possibly the deprecation reason, possibly the called macro, the macro parameters, and possibly the definition VulkanHppGenerator::MacroData parseMacro( std::vector const & completeMacro ) { // #define macro definition // #define macro( params ) definition // #define macro1 macro2( params ) auto const paramsRegex = std::regex{ R"((\(.*?\)))" }; auto const commentRegex = std::regex{ R"(\s*//.*)" }; auto rawComment = completeMacro[0]; std::erase( rawComment, '/' ); auto const strippedComment = trim( stripPostfix( stripPrefix( rawComment, " DEPRECATED:" ), "#define " ) ); // special case for VK_USE_64_BIT_PTR_DEFINES if ( completeMacro.size() == 1 && completeMacro.front().find( "#ifndef VK_USE_64_BIT_PTR_DEFINES" ) != std::string::npos ) { return { {}, {}, {}, strippedComment }; } // macro with parameters and implementation if ( completeMacro.size() == 3 ) { auto const & paramsAndDefinitionAndTrailingComment = completeMacro[2]; if ( paramsAndDefinitionAndTrailingComment.find( '(' ) == std::string::npos ) { // no opening parenthesis found => no parameters return { strippedComment, {}, {}, std::regex_replace( paramsAndDefinitionAndTrailingComment, commentRegex, "" ) }; } // match the first set of parentheses only auto paramsMatch = std::smatch{}; std::regex_search( paramsAndDefinitionAndTrailingComment, paramsMatch, paramsRegex ); // remove the leading and trailing parentheses and tokenise the remaining string auto params = tokenize( stripPrefix( stripPostfix( paramsMatch[1].str(), ")" ), "(" ), "," ); // replace the parameters with empty string, leaving behind the implementation and (possibly) a trailing comment auto implementation = std::regex_replace( paramsAndDefinitionAndTrailingComment, paramsRegex, "", std::regex_constants::format_first_only ); implementation = implementation.substr( 0, implementation.find( "//" ) ); std::erase( implementation, '\\' ); implementation = trim( implementation ); return { strippedComment, {}, params, implementation }; } if ( completeMacro.size() == 4 ) { auto const & calledMacro = toCamelCase( stripPrefix( completeMacro[2], "VK_" ) ); auto const & argsAndTrailingComment = completeMacro[3]; auto argsMatch = std::smatch{}; std::regex_search( argsAndTrailingComment, argsMatch, paramsRegex ); auto args = tokenize( stripPrefix( stripPostfix( argsMatch[1].str(), ")" ), "(" ), "," ); return { strippedComment, calledMacro, args, {} }; } return {}; } std::string startLowerCase( std::string const & input ) { assert( !input.empty() ); return static_cast( tolower( input[0] ) ) + input.substr( 1 ); } std::string startUpperCase( std::string const & input ) { assert( !input.empty() ); return static_cast( toupper( input[0] ) ) + input.substr( 1 ); } std::vector tokenizeAny( std::string const & tokenString, std::string const & separators ) { const size_t len = tokenString.length(); std::vector tokens; if ( !tokenString.empty() ) { size_t start = 0, end; do { end = tokenString.find_first_of( separators, start ); if ( ( start != end ) && ( start < len ) ) { tokens.push_back( trim( tokenString.substr( start, end - start ) ) ); } start = end + 1; } while ( end != std::string::npos ); } return tokens; } } // namespace int main( int argc, char const ** argv ) { if ( ( argc % 2 ) == 0 ) { std::cout << "VulkanHppGenerator usage: VulkanHppGenerator [-f filename][-api [vulkan|vulkanbase|vulkansc]]" << std::endl; std::cout << "\tdefault for filename is <" << VK_SPEC << ">" << std::endl; std::cout << "\tdefault for api " << std::endl; std::cout << "\tsupported values for api are , , and " << std::endl; return -1; } std::string api = "vulkan"; std::string filename = VK_SPEC; for ( int i = 1; i < argc; i += 2 ) { if ( strcmp( argv[i], "-api" ) == 0 ) { api = argv[i + 1]; } else if ( strcmp( argv[i], "-f" ) == 0 ) { filename = argv[i + 1]; } else { std::cout << "unsupported argument <" << argv[i] << ">" << std::endl; return -1; } } if ( ( api != "vulkan" ) && ( api != "vulkanbase" ) && ( api != "vulkansc" ) ) { std::cout << "unsupported api <" << api << ">" << std::endl; return -1; } #if defined( CLANG_FORMAT_EXECUTABLE ) std::cout << "VulkanHppGenerator: Found "; std::string commandString = "\"" CLANG_FORMAT_EXECUTABLE "\" --version "; const int ret = std::system( commandString.c_str() ); if ( ret != 0 ) { std::cout << "VulkanHppGenerator: failed to determine clang_format version with error <" << ret << ">\n"; } #else std::cout << "VulkanHppGenerator: could not find clang-format. The generated files will not be formatted accordingly.\n"; #endif tinyxml2::XMLDocument doc; std::cout << "VulkanHppGenerator: Loading " << filename << std::endl; const tinyxml2::XMLError error = doc.LoadFile( filename.c_str() ); if ( error != tinyxml2::XML_SUCCESS ) { std::cout << "VulkanHppGenerator: failed to load file " << filename << " with error <" << toString( error ) << ">" << std::endl; return -1; } try { std::cout << "VulkanHppGenerator: Parsing " << filename << std::endl; VulkanHppGenerator generator( doc, api ); generator.prepareRAIIHandles(); generator.generateCppmFile(); generator.generateHppFile(); generator.generateEnumsHppFile(); generator.generateExtensionInspectionFile(); generator.generateFormatTraitsHppFile(); generator.generateFuncsHppFile(); generator.generateHandlesHppFile(); generator.generateHashHppFile(); generator.generateMacrosFile(); generator.generateSharedHppFile(); generator.generateStaticAssertionsHppFile(); generator.generateStructsHppFile(); generator.generateToStringHppFile(); // this modifies the generator data and needs to be done after all the other generations are done generator.distributeSecondLevelCommands(); generator.generateRAIIHppFile(); } catch ( std::exception const & e ) { std::cout << "caught exception: " << e.what() << std::endl; return -1; } catch ( ... ) { std::cout << "caught unknown exception" << std::endl; return -1; } }