mirror of
https://github.com/KhronosGroup/Vulkan-Hpp.git
synced 2024-10-14 16:32:17 +00:00
Introduce VideoHppGenerator. (#1618)
This commit is contained in:
parent
23d6bf01e2
commit
069c3b875e
@ -38,6 +38,8 @@ else()
|
||||
cmake_minimum_required( VERSION 3.12 )
|
||||
endif()
|
||||
|
||||
project( VulkanHppGenerator LANGUAGES CXX )
|
||||
|
||||
function( vulkan_hpp__setup_platform )
|
||||
set( options )
|
||||
set( oneValueArgs NAME )
|
||||
@ -348,20 +350,24 @@ if( VULKAN_HPP_ENABLE_EXPERIMENTAL_CPP20_MODULES )
|
||||
target_include_directories( VulkanHppModule PUBLIC "${CMAKE_SOURCE_DIR}/Vulkan-Headers/include" )
|
||||
endif()
|
||||
|
||||
# The generator project !
|
||||
project( VulkanHppGenerator LANGUAGES CXX )
|
||||
# The generator executable
|
||||
add_executable( VulkanHppGenerator VulkanHppGenerator.cpp VulkanHppGenerator.hpp XMLHelper.hpp ${TINYXML2_SOURCES} ${TINYXML2_HEADERS} )
|
||||
vulkan_hpp__setup_warning_level( NAME VulkanHppGenerator )
|
||||
target_compile_definitions( VulkanHppGenerator PUBLIC BASE_PATH="${CMAKE_SOURCE_DIR}" VK_SPEC="${vk_spec}" )
|
||||
target_include_directories( VulkanHppGenerator PRIVATE ${VULKAN_HPP_TINYXML2_SRC_DIR} )
|
||||
set_target_properties( VulkanHppGenerator PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON )
|
||||
|
||||
add_executable( ${PROJECT_NAME} VulkanHppGenerator.cpp VulkanHppGenerator.hpp ${TINYXML2_SOURCES} ${TINYXML2_HEADERS} )
|
||||
# The video generator executable
|
||||
add_executable( VideoHppGenerator VideoHppGenerator.cpp VideoHppGenerator.hpp XMLHelper.hpp ${TINYXML2_SOURCES} ${TINYXML2_HEADERS} )
|
||||
vulkan_hpp__setup_warning_level( NAME VideoHppGenerator )
|
||||
file( TO_NATIVE_PATH ${VulkanRegistry_DIR}/video.xml video_spec )
|
||||
string( REPLACE "\\" "\\\\" video_spec ${video_spec} )
|
||||
target_compile_definitions( VideoHppGenerator PUBLIC BASE_PATH="${CMAKE_SOURCE_DIR}" VIDEO_SPEC="${video_spec}" )
|
||||
target_include_directories( VideoHppGenerator PRIVATE ${VULKAN_HPP_TINYXML2_SRC_DIR} )
|
||||
set_target_properties( VideoHppGenerator PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON )
|
||||
|
||||
vulkan_hpp__setup_warning_level( NAME ${PROJECT_NAME} )
|
||||
|
||||
target_compile_definitions( ${PROJECT_NAME} PUBLIC BASE_PATH="${CMAKE_SOURCE_DIR}" VK_SPEC="${vk_spec}" )
|
||||
|
||||
target_include_directories( ${PROJECT_NAME} PRIVATE ${VULKAN_HPP_TINYXML2_SRC_DIR} )
|
||||
|
||||
set_target_properties( ${PROJECT_NAME} PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON )
|
||||
|
||||
# if the generator is to be run, add a custom command and target
|
||||
# if the generators are to be run, add a custom commands and targets
|
||||
if( VULKAN_HPP_RUN_GENERATOR )
|
||||
if( NOT DEFINED VulkanHeaders_INCLUDE_DIR )
|
||||
if( DEFINED VULKAN_HPP_PATH )
|
||||
@ -374,14 +380,21 @@ if( VULKAN_HPP_RUN_GENERATOR )
|
||||
string( REPLACE "\\" "\\\\" vulkan_hpp ${vulkan_hpp} )
|
||||
|
||||
add_custom_command(
|
||||
COMMAND ${PROJECT_NAME}
|
||||
COMMAND ${PROJECT_NAME} -api vulkansc
|
||||
COMMAND VulkanHppGenerator
|
||||
COMMAND VulkanHppGenerator -api vulkansc
|
||||
OUTPUT "${vulkan_hpp}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
COMMENT "run ${PROJECT_NAME}"
|
||||
DEPENDS ${PROJECT_NAME} "${vk_spec}" )
|
||||
|
||||
COMMENT "run VulkanHppGenerator"
|
||||
DEPENDS VulkanHppGenerator "${vk_spec}" )
|
||||
add_custom_target( build_vulkan_hpp ALL DEPENDS "${vulkan_hpp}" "${vk_spec}" )
|
||||
|
||||
add_custom_command(
|
||||
COMMAND VideoHppGenerator
|
||||
OUTPUT "${vulkan_video_hpp}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
COMMENT "run VulkanVideoGenerator"
|
||||
DEPENDS VulkanVideoGenerator "${video_spec}" )
|
||||
add_custom_target( build_vulkan_video ALL DEPENDS "${vulkan_video_hpp}" "${video_spec}" )
|
||||
endif()
|
||||
|
||||
if( VULKAN_HPP_SAMPLES_BUILD )
|
||||
|
978
VideoHppGenerator.cpp
Normal file
978
VideoHppGenerator.cpp
Normal file
@ -0,0 +1,978 @@
|
||||
// Copyright(c) 2023, 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 "VideoHppGenerator.hpp"
|
||||
|
||||
#include "XMLHelper.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
VideoHppGenerator::VideoHppGenerator( tinyxml2::XMLDocument const & document )
|
||||
{
|
||||
// read the document and check its correctness
|
||||
int line = document.GetLineNum();
|
||||
std::vector<tinyxml2::XMLElement const *> elements = getChildElements( &document );
|
||||
checkElements( line, elements, { { "registry", true } } );
|
||||
checkForError( elements.size() == 1, line, "encountered " + std::to_string( elements.size() ) + " elements named <registry> but only one is allowed" );
|
||||
readRegistry( elements[0] );
|
||||
addImplicitlyRequiredTypes();
|
||||
sortStructs();
|
||||
checkCorrectness();
|
||||
}
|
||||
|
||||
void VideoHppGenerator::generateHppFile() const
|
||||
{
|
||||
std::string const video_hpp = std::string( BASE_PATH ) + "/vulkan/" + "vulkan_video.hpp";
|
||||
std::cout << "VideoHppGenerator: Generating " << video_hpp << " ... " << std::endl;
|
||||
|
||||
std::string const videoHppTemplate = R"(${copyrightMessage}
|
||||
#include <vk_video/vulkan_video_codecs_common.h>
|
||||
#include <vk_video/vulkan_video_codec_h264std.h>
|
||||
#include <vk_video/vulkan_video_codec_h264std_decode.h>
|
||||
#include <vk_video/vulkan_video_codec_h264std_encode.h>
|
||||
#include <vk_video/vulkan_video_codec_h265std.h>
|
||||
#include <vk_video/vulkan_video_codec_h265std_decode.h>
|
||||
#include <vk_video/vulkan_video_codec_h265std_encode.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#if !defined( VULKAN_HPP_VIDEO_NAMESPACE )
|
||||
# define VULKAN_HPP_VIDEO_NAMESPACE video
|
||||
#endif
|
||||
|
||||
namespace VULKAN_HPP_NAMESPACE
|
||||
{
|
||||
namespace VULKAN_HPP_VIDEO_NAMESPACE
|
||||
{
|
||||
${enums}
|
||||
${structs}
|
||||
} // namespace VULKAN_HPP_VIDEO_NAMESPACE
|
||||
} // namespace VULKAN_HPP_NAMESPACE
|
||||
#endif
|
||||
)";
|
||||
|
||||
std::string str =
|
||||
replaceWithMap( videoHppTemplate, { { "copyrightMessage", m_copyrightMessage }, { "enums", generateEnums() }, { "structs", generateStructs() } } );
|
||||
|
||||
writeToFile( str, video_hpp );
|
||||
}
|
||||
|
||||
void VideoHppGenerator::addImplicitlyRequiredTypes()
|
||||
{
|
||||
for ( auto & ext : m_extensions )
|
||||
{
|
||||
for ( auto reqIt = ext.requireData.types.begin(); reqIt != ext.requireData.types.end(); ++reqIt )
|
||||
{
|
||||
std::string name = *reqIt;
|
||||
auto typeIt = m_types.find( *reqIt );
|
||||
if ( ( typeIt != m_types.end() ) && ( typeIt->second.category == TypeCategory::Struct ) )
|
||||
{
|
||||
assert( typeIt->second.requiredBy.find( ext.name ) != typeIt->second.requiredBy.end() );
|
||||
reqIt = addImplicitlyRequiredTypes( typeIt, ext, reqIt );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator VideoHppGenerator::addImplicitlyRequiredTypes( std::map<std::string, TypeData>::iterator typeIt,
|
||||
ExtensionData & extensionData,
|
||||
std::vector<std::string>::iterator reqIt )
|
||||
{
|
||||
auto structIt = m_structs.find( typeIt->first );
|
||||
assert( structIt != m_structs.end() );
|
||||
for ( auto const & member : structIt->second.members )
|
||||
{
|
||||
auto memberTypeIt = m_types.find( member.type.type );
|
||||
if ( ( memberTypeIt != m_types.end() ) && ( memberTypeIt->second.category == TypeCategory::Struct ) )
|
||||
{
|
||||
reqIt = addImplicitlyRequiredTypes( memberTypeIt, extensionData, reqIt );
|
||||
}
|
||||
}
|
||||
assert( typeIt->second.requiredBy.empty() || ( *typeIt->second.requiredBy.begin() == extensionData.name ) ||
|
||||
( *typeIt->second.requiredBy.begin() == extensionData.depends ) );
|
||||
if ( typeIt->second.requiredBy.empty() && ( std::find( extensionData.requireData.types.begin(), reqIt, typeIt->first ) == reqIt ) )
|
||||
{
|
||||
assert( std::find( reqIt, extensionData.requireData.types.end(), typeIt->first ) == extensionData.requireData.types.end() );
|
||||
typeIt->second.requiredBy.insert( extensionData.name );
|
||||
reqIt = std::next( extensionData.requireData.types.insert( reqIt, typeIt->first ) );
|
||||
}
|
||||
return reqIt;
|
||||
}
|
||||
|
||||
void VideoHppGenerator::checkCorrectness() const
|
||||
{
|
||||
// only structs to check here!
|
||||
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 extension" );
|
||||
|
||||
assert( typeIt->second.requiredBy.size() == 1 );
|
||||
auto extIt =
|
||||
std::find_if( m_extensions.begin(), m_extensions.end(), [&typeIt]( ExtensionData const & ed ) { return ed.name == *typeIt->second.requiredBy.begin(); } );
|
||||
assert( extIt != m_extensions.end() );
|
||||
|
||||
// checks on the members of a struct
|
||||
for ( auto const & member : structure.second.members )
|
||||
{
|
||||
// check that each member type is known
|
||||
checkForError( m_types.find( member.type.type ) != m_types.end(), member.xmlLine, "struct member uses unknown type <" + member.type.type + ">" );
|
||||
|
||||
// check that all member types are required in some extension (it's just a warning!!)
|
||||
if ( member.type.type.starts_with( "StdVideo" ) )
|
||||
{
|
||||
auto memberTypeIt = m_types.find( member.type.type );
|
||||
assert( memberTypeIt != m_types.end() );
|
||||
checkForWarning( !memberTypeIt->second.requiredBy.empty(),
|
||||
member.xmlLine,
|
||||
"struct member type <" + member.type.type + "> used in struct <" + structure.first + "> is never required for any extension" );
|
||||
}
|
||||
|
||||
// check that all array sizes are a known constant
|
||||
for ( auto const & arraySize : member.arraySizes )
|
||||
{
|
||||
if ( arraySize.find_first_not_of( "0123456789" ) != std::string::npos )
|
||||
{
|
||||
bool found = ( extIt->requireData.constants.find( arraySize ) != extIt->requireData.constants.end() );
|
||||
if ( !found )
|
||||
{
|
||||
checkForError(
|
||||
!extIt->depends.empty(), extIt->xmlLine, "struct member <" + member.name + "> uses unknown constant <" + arraySize + "> as array size" );
|
||||
auto depIt = std::find_if( m_extensions.begin(), m_extensions.end(), [&extIt]( ExtensionData const & ed ) { return ed.name == extIt->depends; } );
|
||||
assert( depIt != m_extensions.end() );
|
||||
checkForError( depIt->requireData.constants.find( arraySize ) != depIt->requireData.constants.end(),
|
||||
member.xmlLine,
|
||||
"struct member <" + member.name + "> uses unknown constant <" + arraySize + "> as array size" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateEnum( std::pair<std::string, EnumData> const & enumData ) const
|
||||
{
|
||||
std::string enumValues;
|
||||
#if !defined( NDEBUG )
|
||||
std::map<std::string, std::string> valueToNameMap;
|
||||
#endif
|
||||
|
||||
// convert the enum name to upper case
|
||||
std::string prefix = toUpperCase( enumData.first ) + "_";
|
||||
for ( auto const & value : enumData.second.values )
|
||||
{
|
||||
std::string valueName = "e" + toCamelCase( stripPrefix( value.name, prefix ), true );
|
||||
assert( valueToNameMap.insert( { valueName, value.name } ).second );
|
||||
enumValues += " " + valueName + " = " + value.name + ",\n";
|
||||
}
|
||||
|
||||
if ( !enumValues.empty() )
|
||||
{
|
||||
size_t pos = enumValues.rfind( ',' );
|
||||
assert( pos != std::string::npos );
|
||||
enumValues.erase( pos, 1 );
|
||||
enumValues = "\n" + enumValues + " ";
|
||||
}
|
||||
|
||||
const std::string enumTemplate = R"( enum class ${enumName}
|
||||
{${enumValues}};
|
||||
)";
|
||||
|
||||
return replaceWithMap( enumTemplate, { { "enumName", stripPrefix( enumData.first, "StdVideo" ) }, { "enumValues", enumValues } } );
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateEnums() const
|
||||
{
|
||||
{
|
||||
const std::string enumsTemplate = R"(
|
||||
//=============
|
||||
//=== ENUMs ===
|
||||
//=============
|
||||
|
||||
${enums}
|
||||
)";
|
||||
|
||||
std::string enums;
|
||||
for ( auto const & extension : m_extensions )
|
||||
{
|
||||
enums += generateEnums( extension.requireData, extension.name );
|
||||
}
|
||||
|
||||
return replaceWithMap( enumsTemplate, { { "enums", enums } } );
|
||||
}
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateEnums( RequireData const & requireData, std::string const & title ) const
|
||||
{
|
||||
std::string str;
|
||||
for ( auto const & type : requireData.types )
|
||||
{
|
||||
auto enumIt = m_enums.find( type );
|
||||
if ( enumIt != m_enums.end() )
|
||||
{
|
||||
str += "\n" + generateEnum( *enumIt );
|
||||
}
|
||||
}
|
||||
if ( !str.empty() )
|
||||
{
|
||||
str = "\n //=== " + title + " ===\n" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateStruct( std::pair<std::string, StructureData> const & structData ) const
|
||||
{
|
||||
static const std::string structureTemplate = R"( struct ${structureType}
|
||||
{
|
||||
using NativeType = StdVideo${structureType};
|
||||
|
||||
operator StdVideo${structureType} const &() const VULKAN_HPP_NOEXCEPT
|
||||
{
|
||||
return *reinterpret_cast<const StdVideo${structureType}*>( this );
|
||||
}
|
||||
|
||||
operator StdVideo${structureType} &() VULKAN_HPP_NOEXCEPT
|
||||
{
|
||||
return *reinterpret_cast<StdVideo${structureType}*>( this );
|
||||
}
|
||||
${compareOperators}
|
||||
public:
|
||||
${members}
|
||||
};
|
||||
)";
|
||||
|
||||
return replaceWithMap( structureTemplate,
|
||||
{ { "compareOperators", generateStructCompareOperators( structData ) },
|
||||
{ "members", generateStructMembers( structData ) },
|
||||
{ "structureType", stripPrefix( structData.first, "StdVideo" ) } } );
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateStructCompareOperators( std::pair<std::string, StructureData> const & structData ) const
|
||||
{
|
||||
static const std::set<std::string> 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;
|
||||
std::string intro = "";
|
||||
for ( size_t i = 0; i < structData.second.members.size(); i++ )
|
||||
{
|
||||
MemberData const & member = structData.second.members[i];
|
||||
auto typeIt = m_types.find( member.type.type );
|
||||
assert( typeIt != m_types.end() );
|
||||
if ( ( typeIt->second.category == TypeCategory::ExternalType ) && member.type.postfix.empty() &&
|
||||
( simpleTypes.find( member.type.type ) == simpleTypes.end() ) )
|
||||
{
|
||||
// this type might support operator==() or operator<=>()... that is, use memcmp
|
||||
compareMembers += intro + "( memcmp( &" + member.name + ", &rhs." + member.name + ", sizeof( " + member.type.type + " ) ) == 0 )";
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( member.type.type != "char" );
|
||||
// for all others, we use the operator== of that type
|
||||
compareMembers += intro + "( " + member.name + " == rhs." + member.name + " )";
|
||||
}
|
||||
intro = "\n && ";
|
||||
}
|
||||
|
||||
static const std::string compareTemplate = R"(
|
||||
bool operator==( ${name} const & rhs ) const VULKAN_HPP_NOEXCEPT
|
||||
{
|
||||
return ${compareMembers};
|
||||
}
|
||||
|
||||
bool operator!=( ${name} const & rhs ) const VULKAN_HPP_NOEXCEPT
|
||||
{
|
||||
return !operator==( rhs );
|
||||
}
|
||||
)";
|
||||
|
||||
return replaceWithMap( compareTemplate, { { "name", stripPrefix( structData.first, "StdVideo" ) }, { "compareMembers", compareMembers } } );
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateStructMembers( std::pair<std::string, StructureData> const & structData ) const
|
||||
{
|
||||
std::string members;
|
||||
for ( auto const & member : structData.second.members )
|
||||
{
|
||||
members += " ";
|
||||
std::string type;
|
||||
if ( !member.bitCount.empty() && member.type.type.starts_with( "StdVideo" ) )
|
||||
{
|
||||
assert( member.type.prefix.empty() && member.type.postfix.empty() ); // never encounterd a different case
|
||||
type = member.type.type;
|
||||
}
|
||||
else if ( member.arraySizes.empty() )
|
||||
{
|
||||
type = member.type.compose( "VULKAN_HPP_NAMESPACE::VULKAN_HPP_VIDEO_NAMESPACE", "StdVideo" );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( member.type.prefix.empty() && member.type.postfix.empty() );
|
||||
type = generateStandardArrayWrapper( member.type.compose( "VULKAN_HPP_NAMESPACE::VULKAN_HPP_VIDEO_NAMESPACE" ), member.arraySizes );
|
||||
}
|
||||
members += type + " " + member.name;
|
||||
|
||||
// as we don't have any meaningful default initialization values, everything can be initialized by just '{}' !
|
||||
assert( member.arraySizes.empty() || member.bitCount.empty() );
|
||||
if ( !member.bitCount.empty() )
|
||||
{
|
||||
members += " : " + member.bitCount; // except for bitfield members, where no default member initialization
|
||||
// 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() )
|
||||
{
|
||||
assert( member.type.prefix.empty() && member.arraySizes.empty() && !enumIt->second.values.empty() );
|
||||
|
||||
std::string prefix = toUpperCase( member.type.type ) + "_";
|
||||
std::string valueName = "e" + toCamelCase( stripPrefix( enumIt->second.values.front().name, prefix ), true );
|
||||
|
||||
members += type + "::" + valueName;
|
||||
}
|
||||
else
|
||||
{
|
||||
members += "{}";
|
||||
}
|
||||
}
|
||||
members += ";\n";
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateStructs() const
|
||||
{
|
||||
const std::string structsTemplate = R"(
|
||||
//===============
|
||||
//=== STRUCTS ===
|
||||
//===============
|
||||
|
||||
${structs}
|
||||
)";
|
||||
|
||||
std::string structs;
|
||||
for ( auto const & extension : m_extensions )
|
||||
{
|
||||
structs += generateStructs( extension.requireData, extension.name );
|
||||
}
|
||||
return replaceWithMap( structsTemplate, { { "structs", structs } } );
|
||||
}
|
||||
|
||||
std::string VideoHppGenerator::generateStructs( RequireData const & requireData, std::string const & title ) const
|
||||
{
|
||||
std::string str;
|
||||
for ( auto const & type : requireData.types )
|
||||
{
|
||||
auto structIt = m_structs.find( type );
|
||||
if ( structIt != m_structs.end() )
|
||||
{
|
||||
str += "\n" + generateStruct( *structIt );
|
||||
}
|
||||
}
|
||||
if ( !str.empty() )
|
||||
{
|
||||
str = "\n //=== " + title + " ===\n" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
bool VideoHppGenerator::isExtension( std::string const & name ) const
|
||||
{
|
||||
return std::find_if( m_extensions.begin(), m_extensions.end(), [&name]( ExtensionData const & ed ) { return ed.name == name; } ) != m_extensions.end();
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readEnums( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
checkAttributes( line, attributes, { { "name", {} }, { "type", { "enum" } } }, {} );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "enum", {} } }, { "comment" } );
|
||||
|
||||
std::string name, type;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "name" )
|
||||
{
|
||||
name = attribute.second;
|
||||
}
|
||||
else if ( attribute.first == "type" )
|
||||
{
|
||||
type = attribute.second;
|
||||
}
|
||||
}
|
||||
|
||||
// get the EnumData entry in enum map
|
||||
auto enumIt = m_enums.find( 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" );
|
||||
|
||||
// read the names of the enum values
|
||||
for ( auto child : children )
|
||||
{
|
||||
std::string value = child->Value();
|
||||
if ( value == "enum" )
|
||||
{
|
||||
readEnumsEnum( child, enumIt );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readEnumsEnum( tinyxml2::XMLElement const * element, std::map<std::string, EnumData>::iterator enumIt )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
checkAttributes( line, attributes, { { "name", {} }, { "value", {} } }, { { "comment", {} } } );
|
||||
checkElements( line, getChildElements( element ), {} );
|
||||
|
||||
std::string name, value;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "name" )
|
||||
{
|
||||
name = attribute.second;
|
||||
}
|
||||
else if ( attribute.first == "value" )
|
||||
{
|
||||
value = attribute.second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string prefix = toUpperCase( enumIt->first ) + "_";
|
||||
checkForError( name.starts_with( prefix ), line, "encountered enum value <" + name + "> that does not begin with expected prefix <" + prefix + ">" );
|
||||
|
||||
checkForError( std::find_if( enumIt->second.values.begin(),
|
||||
enumIt->second.values.end(),
|
||||
[&name]( EnumValueData const & evd ) { return evd.name == name; } ) == enumIt->second.values.end(),
|
||||
line,
|
||||
"enum value <" + name + "> already part of enum <" + enumIt->first + ">" );
|
||||
enumIt->second.values.push_back( { name, value, line } );
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readExtension( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
|
||||
checkAttributes( line, attributes, { { "name", {} }, { "comment", {} }, { "supported", { "vulkan" } } }, {} );
|
||||
checkElements( line, children, { { "require", false } } );
|
||||
|
||||
ExtensionData extensionData{ .xmlLine = line };
|
||||
std::string supported;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "name" )
|
||||
{
|
||||
extensionData.name = attribute.second;
|
||||
checkForError( !isExtension( extensionData.name ), line, "already encountered extension <" + extensionData.name + ">" );
|
||||
}
|
||||
else if ( attribute.first == "supported" )
|
||||
{
|
||||
supported = attribute.second;
|
||||
}
|
||||
}
|
||||
checkForError( supported == "vulkan", line, "extension <" + extensionData.name + "> has unknown supported type <" + supported + ">" );
|
||||
|
||||
for ( auto child : children )
|
||||
{
|
||||
const std::string value = child->Value();
|
||||
assert( value == "require" );
|
||||
readExtensionRequire( child, extensionData );
|
||||
}
|
||||
|
||||
m_extensions.push_back( extensionData );
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readExtensionRequire( tinyxml2::XMLElement const * element, ExtensionData & extensionData )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
checkAttributes( line, attributes, {}, {} );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, {}, { "enum", "type" } );
|
||||
|
||||
extensionData.requireData.xmlLine = line;
|
||||
|
||||
for ( auto child : children )
|
||||
{
|
||||
std::string value = child->Value();
|
||||
if ( value == "enum" )
|
||||
{
|
||||
readRequireEnum( child, extensionData.requireData.constants );
|
||||
}
|
||||
else if ( value == "type" )
|
||||
{
|
||||
readRequireType( child, extensionData );
|
||||
}
|
||||
}
|
||||
assert( !extensionData.requireData.types.empty() );
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readExtensions( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, getAttributes( element ), {}, {} );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "extension", false } } );
|
||||
|
||||
for ( auto child : children )
|
||||
{
|
||||
const std::string value = child->Value();
|
||||
assert( value == "extension" );
|
||||
readExtension( child );
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readRegistry( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, getAttributes( element ), {}, {} );
|
||||
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "comment", false }, { "enums", false }, { "extensions", true }, { "types", true } } );
|
||||
for ( auto child : children )
|
||||
{
|
||||
const std::string value = child->Value();
|
||||
if ( value == "comment" )
|
||||
{
|
||||
std::string comment = readComment( child );
|
||||
if ( comment.find( "\nCopyright" ) == 0 )
|
||||
{
|
||||
m_copyrightMessage = generateCopyrightMessage( comment );
|
||||
}
|
||||
}
|
||||
else if ( value == "enums" )
|
||||
{
|
||||
readEnums( child );
|
||||
}
|
||||
else if ( value == "extensions" )
|
||||
{
|
||||
readExtensions( child );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( value == "types" );
|
||||
readTypes( child );
|
||||
}
|
||||
}
|
||||
checkForError( !m_copyrightMessage.empty(), -1, "missing copyright message" );
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readRequireEnum( tinyxml2::XMLElement const * element, std::map<std::string, ConstantData> & constants )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
checkElements( line, getChildElements( element ), {} );
|
||||
checkAttributes( line, attributes, { { "name", {} }, { "value", {} } }, {} );
|
||||
|
||||
std::string name, value;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "name" )
|
||||
{
|
||||
name = attribute.second;
|
||||
}
|
||||
else if ( attribute.first == "value" )
|
||||
{
|
||||
value = attribute.second;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !name.ends_with( "_SPEC_VERSION" ) && !name.ends_with( "_EXTENSION_NAME" ) )
|
||||
{
|
||||
checkForError( value.find_first_not_of( "0123456789" ) == std::string::npos, line, "enum value uses unknown constant <" + value + ">" );
|
||||
checkForError( constants.insert( { name, { value, line } } ).second, line, "required enum <" + name + "> already specified" );
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readRequireType( tinyxml2::XMLElement const * element, ExtensionData & extensionData )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
checkAttributes( line, attributes, { { "name", {} } }, { { "comment", {} } } );
|
||||
checkElements( line, getChildElements( element ), {} );
|
||||
|
||||
std::string name = attributes.find( "name" )->second;
|
||||
if ( name.starts_with( "vk_video/vulkan_video_codec" ) && name.ends_with( ".h" ) )
|
||||
{
|
||||
checkForError( extensionData.depends.empty(), line, "extension <" + extensionData.name + "> already depends on <" + extensionData.name + ">" );
|
||||
extensionData.depends = stripPrefix( stripPostfix( name, ".h" ), "vk_video/" );
|
||||
checkForError( isExtension( extensionData.depends ), line, "extension <" + extensionData.name + "> uses unknown header <" + name + ">" );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto typeIt = m_types.find( name );
|
||||
checkForError( typeIt != m_types.end(), line, "unknown required type <" + name + ">" );
|
||||
typeIt->second.requiredBy.insert( extensionData.name );
|
||||
extensionData.requireData.types.push_back( name );
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readStructMember( tinyxml2::XMLElement const * element, std::vector<MemberData> & members )
|
||||
{
|
||||
(void)members;
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
checkAttributes( line, attributes, {}, {} );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "name", true }, { "type", true } }, { "comment", "enum" } );
|
||||
|
||||
MemberData memberData;
|
||||
memberData.xmlLine = line;
|
||||
|
||||
std::string name;
|
||||
for ( auto child : children )
|
||||
{
|
||||
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() && child->NextSibling(), line, "struct member array specification is ill-formatted: <" + enumString + ">" );
|
||||
std::string previous = child->PreviousSibling()->Value();
|
||||
std::string next = child->NextSibling()->Value();
|
||||
checkForError( previous.ends_with( "[" ) && next.starts_with( "]" ), line, "struct member array specification 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( std::find_if( members.begin(), members.end(), [&name]( MemberData const & md ) { return md.name == name; } ) == members.end(),
|
||||
line,
|
||||
"struct member name <" + name + "> already used" );
|
||||
memberData.name = name;
|
||||
members.push_back( memberData );
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypeDefine( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, attributes, { { "category", { "define" } } }, { { "requires", {} } } );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "name", false } }, { "type" } );
|
||||
|
||||
std::string require;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "requires" )
|
||||
{
|
||||
require = attribute.second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string name, type;
|
||||
for ( auto child : children )
|
||||
{
|
||||
const std::string value = child->Value();
|
||||
if ( value == "name" )
|
||||
{
|
||||
name = child->GetText();
|
||||
}
|
||||
else if ( value == "type" )
|
||||
{
|
||||
type = child->GetText();
|
||||
}
|
||||
}
|
||||
checkForError( require.empty() || ( m_defines.find( require ) != m_defines.end() ), line, "define <" + name + "> requires unknown type <" + require + ">" );
|
||||
checkForError( type.empty() || ( m_defines.find( type ) != m_defines.end() ), line, "define <" + name + "> of unknown type <" + type + ">" );
|
||||
|
||||
checkForError( m_types.insert( { name, TypeData{ TypeCategory::Define, {}, line } } ).second, line, "define <" + name + "> already specified" );
|
||||
assert( m_defines.find( name ) == m_defines.end() );
|
||||
m_defines[name] = { require, line };
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypeEnum( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, attributes, { { "category", { "enum" } }, { "name", {} } }, {} );
|
||||
checkElements( line, getChildElements( element ), {} );
|
||||
|
||||
std::string name;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "name" )
|
||||
{
|
||||
name = attribute.second;
|
||||
}
|
||||
}
|
||||
|
||||
checkForError( m_types.insert( { name, TypeData{ TypeCategory::Enum, {}, line } } ).second, line, "enum <" + name + "> already specified" );
|
||||
assert( m_enums.find( name ) == m_enums.end() );
|
||||
m_enums[name] = EnumData{ .xmlLine = line };
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypeInclude( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes )
|
||||
{
|
||||
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.find( name ) == m_includes.end() );
|
||||
m_includes[name] = { line };
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypeRequires( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes )
|
||||
{
|
||||
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.find( require ) != m_includes.end(), line, "type <" + name + "> requires unknown <" + require + ">" );
|
||||
checkForError( m_types.insert( { name, TypeData{ TypeCategory::ExternalType, {}, line } } ).second, line, "type <" + name + "> already specified" );
|
||||
assert( m_externalTypes.find( name ) == m_externalTypes.end() );
|
||||
m_externalTypes[name] = { require, line };
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypes( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, getAttributes( element ), { { "comment", {} } }, {} );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "type", false } } );
|
||||
|
||||
for ( auto child : children )
|
||||
{
|
||||
std::string value = child->Value();
|
||||
if ( value == "type" )
|
||||
{
|
||||
readTypesType( child );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypeStruct( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, attributes, { { "category", { "struct" } }, { "name", {} } }, { { "comment", {} }, { "requires", {} } } );
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
checkElements( line, children, { { "member", false } }, { "comment" } );
|
||||
|
||||
StructureData structureData{ .xmlLine = line };
|
||||
|
||||
std::string name, require;
|
||||
for ( auto const & attribute : attributes )
|
||||
{
|
||||
if ( attribute.first == "name" )
|
||||
{
|
||||
name = attribute.second;
|
||||
}
|
||||
else if ( attribute.first == "requires" )
|
||||
{
|
||||
require = attribute.second;
|
||||
}
|
||||
}
|
||||
assert( !name.empty() );
|
||||
checkForError( require.empty() || ( m_types.find( require ) != m_types.end() ), line, "struct <" + name + "> requires unknown type <" + require + ">" );
|
||||
|
||||
checkForError( m_types.insert( { name, TypeData{ TypeCategory::Struct, {}, line } } ).second, line, "struct <" + name + "> already specified" );
|
||||
assert( m_structs.find( name ) == m_structs.end() );
|
||||
|
||||
std::map<std::string, StructureData>::iterator it = m_structs.insert( std::make_pair( name, structureData ) ).first;
|
||||
|
||||
for ( auto child : children )
|
||||
{
|
||||
std::string value = child->Value();
|
||||
if ( value == "member" )
|
||||
{
|
||||
readStructMember( child, it->second.members );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::readTypesType( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::map<std::string, std::string> attributes = getAttributes( element );
|
||||
|
||||
auto categoryIt = attributes.find( "category" );
|
||||
if ( categoryIt != attributes.end() )
|
||||
{
|
||||
if ( categoryIt->second == "define" )
|
||||
{
|
||||
readTypeDefine( element, attributes );
|
||||
}
|
||||
else if ( categoryIt->second == "enum" )
|
||||
{
|
||||
readTypeEnum( element, attributes );
|
||||
}
|
||||
else if ( categoryIt->second == "include" )
|
||||
{
|
||||
readTypeInclude( element, attributes );
|
||||
}
|
||||
else if ( categoryIt->second == "struct" )
|
||||
{
|
||||
readTypeStruct( element, 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 <int> already specified" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoHppGenerator::sortStructs()
|
||||
{
|
||||
for ( auto & ext : m_extensions )
|
||||
{
|
||||
for ( auto reqIt = ext.requireData.types.begin(); reqIt != ext.requireData.types.end(); ++reqIt )
|
||||
{
|
||||
std::string name = *reqIt;
|
||||
auto typeIt = m_types.find( *reqIt );
|
||||
if ( ( typeIt != m_types.end() ) && ( typeIt->second.category == TypeCategory::Struct ) )
|
||||
{
|
||||
auto structIt = m_structs.find( typeIt->first );
|
||||
assert( structIt != m_structs.end() );
|
||||
for ( auto const & member : structIt->second.members )
|
||||
{
|
||||
auto memberTypeIt = m_types.find( member.type.type );
|
||||
assert( memberTypeIt != m_types.end() );
|
||||
if ( ( memberTypeIt->second.category == TypeCategory::Struct ) && ( std::find( ext.requireData.types.begin(), reqIt, member.type.type ) == reqIt ) )
|
||||
{
|
||||
auto it = std::find( std::next( reqIt ), ext.requireData.types.end(), member.type.type );
|
||||
if ( it != ext.requireData.types.end() )
|
||||
{
|
||||
ext.requireData.types.erase( it );
|
||||
reqIt = std::next( ext.requireData.types.insert( reqIt, member.type.type ) );
|
||||
}
|
||||
#if !defined(NDEBUG)
|
||||
else
|
||||
{
|
||||
auto depIt = std::find_if( m_extensions.begin(), m_extensions.end(), [&ext]( ExtensionData const & ed ) { return ed.name == ext.depends; } );
|
||||
assert( ( depIt != m_extensions.end() ) &&
|
||||
( std::find( depIt->requireData.types.begin(), depIt->requireData.types.end(), member.type.type ) != depIt->requireData.types.end() ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
if ( ( argc % 2 ) == 0 )
|
||||
{
|
||||
std::cout << "VideoHppGenerator usage: VideoHppGenerator [-f filename]" << std::endl;
|
||||
std::cout << "\tdefault for filename is <" << VIDEO_SPEC << ">" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string filename = VIDEO_SPEC;
|
||||
for ( int i = 1; i < argc; i += 2 )
|
||||
{
|
||||
if ( strcmp( argv[i], "-f" ) == 0 )
|
||||
{
|
||||
filename = argv[i + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "unsupported argument <" << argv[i] << ">" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( CLANG_FORMAT_EXECUTABLE )
|
||||
std::cout << "VideoHppGenerator: Found ";
|
||||
std::string commandString = "\"" CLANG_FORMAT_EXECUTABLE "\" --version ";
|
||||
int ret = std::system( commandString.c_str() );
|
||||
if ( ret != 0 )
|
||||
{
|
||||
std::cout << "VideoHppGenerator: failed to determine clang_format version with error <" << ret << ">\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
std::cout << "VideoHppGenerator: Loading " << filename << std::endl;
|
||||
tinyxml2::XMLError error = doc.LoadFile( filename.c_str() );
|
||||
if ( error != tinyxml2::XML_SUCCESS )
|
||||
{
|
||||
std::cout << "VideoHppGenerator: failed to load file " << filename << " with error <" << toString( error ) << ">" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::cout << "VideoHppGenerator: Parsing " << filename << std::endl;
|
||||
VideoHppGenerator generator( doc );
|
||||
|
||||
generator.generateHppFile();
|
||||
|
||||
# if !defined( CLANG_FORMAT_EXECUTABLE )
|
||||
std::cout << "VideoHppGenerator: could not find clang-format. The generated files will not be formatted accordingly.\n";
|
||||
# endif
|
||||
}
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
127
VideoHppGenerator.hpp
Normal file
127
VideoHppGenerator.hpp
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright(c) 2023, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "XMLHelper.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
class VideoHppGenerator
|
||||
{
|
||||
public:
|
||||
VideoHppGenerator( tinyxml2::XMLDocument const & document );
|
||||
|
||||
void generateHppFile() const;
|
||||
|
||||
private:
|
||||
struct ConstantData
|
||||
{
|
||||
std::string value = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct DefineData
|
||||
{
|
||||
std::string require = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct EnumValueData
|
||||
{
|
||||
std::string name = {};
|
||||
std::string value = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct EnumData
|
||||
{
|
||||
std::vector<EnumValueData> values = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct RequireData
|
||||
{
|
||||
std::map<std::string, ConstantData> constants = {};
|
||||
std::vector<std::string> types = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct ExtensionData
|
||||
{
|
||||
std::string depends = {};
|
||||
std::string name = {};
|
||||
RequireData requireData = {};
|
||||
int xmlLine = 0;
|
||||
};
|
||||
|
||||
struct MemberData
|
||||
{
|
||||
TypeInfo type = {};
|
||||
std::string name = {};
|
||||
std::vector<std::string> arraySizes = {};
|
||||
std::string bitCount = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct StructureData
|
||||
{
|
||||
std::vector<MemberData> members = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
private:
|
||||
void addImplicitlyRequiredTypes();
|
||||
std::vector<std::string>::iterator
|
||||
addImplicitlyRequiredTypes( std::map<std::string, TypeData>::iterator typeIt, ExtensionData & extensionData, std::vector<std::string>::iterator reqIt );
|
||||
void checkCorrectness() const;
|
||||
std::string generateEnum( std::pair<std::string, EnumData> const & enumData ) const;
|
||||
std::string generateEnums() const;
|
||||
std::string generateEnums( RequireData const & requireData, std::string const & title ) const;
|
||||
std::string generateStruct( std::pair<std::string, StructureData> const & structData ) const;
|
||||
std::string generateStructCompareOperators( std::pair<std::string, StructureData> const & structData ) const;
|
||||
std::string generateStructMembers( std::pair<std::string, StructureData> const & structData ) const;
|
||||
std::string generateStructs() const;
|
||||
std::string generateStructs( RequireData const & requireData, std::string const & title ) const;
|
||||
bool isExtension( std::string const & name ) const;
|
||||
void readEnums( tinyxml2::XMLElement const * element );
|
||||
void readEnumsEnum( tinyxml2::XMLElement const * element, std::map<std::string, EnumData>::iterator enumIt );
|
||||
void readExtension( tinyxml2::XMLElement const * element );
|
||||
void readExtensionRequire( tinyxml2::XMLElement const * element, ExtensionData & extensionData );
|
||||
void readExtensions( tinyxml2::XMLElement const * element );
|
||||
void readRegistry( tinyxml2::XMLElement const * element );
|
||||
void readRequireEnum( tinyxml2::XMLElement const * element, std::map<std::string, ConstantData> & constants );
|
||||
void readRequireType( tinyxml2::XMLElement const * element, ExtensionData & extensionData );
|
||||
void readStructMember( tinyxml2::XMLElement const * element, std::vector<MemberData> & members );
|
||||
void readTypeDefine( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes );
|
||||
void readTypeEnum( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes );
|
||||
void readTypeInclude( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes );
|
||||
void readTypeRequires( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes );
|
||||
void readTypes( tinyxml2::XMLElement const * element );
|
||||
void readTypeStruct( tinyxml2::XMLElement const * element, std::map<std::string, std::string> const & attributes );
|
||||
void readTypesType( tinyxml2::XMLElement const * element );
|
||||
void sortStructs();
|
||||
|
||||
private:
|
||||
std::string m_copyrightMessage;
|
||||
std::map<std::string, DefineData> m_defines;
|
||||
std::map<std::string, EnumData> m_enums;
|
||||
std::vector<ExtensionData> m_extensions;
|
||||
std::map<std::string, ExternalTypeData> m_externalTypes;
|
||||
std::map<std::string, IncludeData> m_includes;
|
||||
std::map<std::string, StructureData> m_structs;
|
||||
std::map<std::string, TypeData> m_types;
|
||||
};
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "VulkanHppGenerator.hpp"
|
||||
|
||||
#include "XMLHelper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
@ -33,10 +35,6 @@ void checkAttributes( int
|
||||
std::map<std::string, std::string> const & attributes,
|
||||
std::map<std::string, std::set<std::string>> const & required,
|
||||
std::map<std::string, std::set<std::string>> const & optional );
|
||||
void checkElements( int line,
|
||||
std::vector<tinyxml2::XMLElement const *> const & elements,
|
||||
std::map<std::string, bool> const & required,
|
||||
std::set<std::string> const & optional = {} );
|
||||
void checkForError( bool condition, int line, std::string const & message );
|
||||
void checkForWarning( bool condition, int line, std::string const & message );
|
||||
std::vector<std::pair<std::string, size_t>> filterNumbers( std::vector<std::string> const & names );
|
||||
@ -56,7 +54,6 @@ std::string startLowerCase( std::string con
|
||||
std::string startUpperCase( std::string const & input );
|
||||
std::string stripPostfix( std::string const & value, std::string const & postfix );
|
||||
std::string stripPrefix( std::string const & value, std::string const & prefix );
|
||||
std::string toCamelCase( std::string const & value );
|
||||
std::string toUpperCase( std::string const & name );
|
||||
std::vector<std::string> tokenize( std::string const & tokenString, std::string const & separator );
|
||||
std::vector<std::string> tokenizeAny( std::string const & tokenString, std::string const & separators );
|
||||
@ -1488,11 +1485,11 @@ void VulkanHppGenerator::checkStructMemberCorrectness( std::string const &
|
||||
checkForError( m_types.find( member.type.type ) != m_types.end(), member.xmlLine, "struct member uses unknown type <" + member.type.type + ">" );
|
||||
|
||||
// check that any used constant is a known constant
|
||||
if ( !member.usedConstant.empty() )
|
||||
for ( auto const & arraySize : member.arraySizes )
|
||||
{
|
||||
checkForError( m_constants.find( member.usedConstant ) != m_constants.end(),
|
||||
checkForError( ( arraySize.find_first_not_of( "0123456789" ) == std::string::npos ) || ( m_constants.find( arraySize ) != m_constants.end() ),
|
||||
member.xmlLine,
|
||||
"struct member array size uses unknown constant <" + member.usedConstant + ">" );
|
||||
"struct member array size uses unknown constant <" + arraySize + ">" );
|
||||
}
|
||||
|
||||
// checks if a value is specified
|
||||
@ -12614,15 +12611,6 @@ void VulkanHppGenerator::readEnumsEnum( tinyxml2::XMLElement const * element, st
|
||||
}
|
||||
}
|
||||
|
||||
std::string VulkanHppGenerator::readComment( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, getAttributes( element ), {}, {} );
|
||||
checkElements( line, getChildElements( element ), {} );
|
||||
|
||||
return element->GetText();
|
||||
}
|
||||
|
||||
void VulkanHppGenerator::readExtensionRequire( tinyxml2::XMLElement const * element, ExtensionData & extensionData, bool extensionSupported )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
@ -13188,7 +13176,7 @@ std::string VulkanHppGenerator::readName( tinyxml2::XMLElement const * element )
|
||||
return attributes.find( "name" )->second;
|
||||
}
|
||||
|
||||
std::pair<VulkanHppGenerator::NameData, VulkanHppGenerator::TypeInfo> VulkanHppGenerator::readNameAndType( tinyxml2::XMLElement const * element )
|
||||
std::pair<VulkanHppGenerator::NameData, TypeInfo> VulkanHppGenerator::readNameAndType( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
std::vector<tinyxml2::XMLElement const *> children = getChildElements( element );
|
||||
@ -13301,7 +13289,7 @@ void VulkanHppGenerator::readRegistry( tinyxml2::XMLElement const * element )
|
||||
std::string comment = readComment( child );
|
||||
if ( comment.find( "\nCopyright" ) == 0 )
|
||||
{
|
||||
setVulkanLicenseHeader( child->GetLineNum(), comment );
|
||||
m_vulkanLicenseHeader = generateCopyrightMessage( comment );
|
||||
}
|
||||
}
|
||||
else if ( value == "enums" )
|
||||
@ -13834,8 +13822,6 @@ void VulkanHppGenerator::readStructMember( tinyxml2::XMLElement const * element,
|
||||
std::string( "struct member array specifiation is ill-formatted: <" ) + enumString + ">" );
|
||||
|
||||
memberData.arraySizes.push_back( enumString );
|
||||
checkForError( memberData.usedConstant.empty(), line, "struct already holds a constant <" + memberData.usedConstant + ">" );
|
||||
memberData.usedConstant = enumString;
|
||||
}
|
||||
else if ( value == "name" )
|
||||
{
|
||||
@ -14713,7 +14699,7 @@ void VulkanHppGenerator::readTypesType( tinyxml2::XMLElement const * element )
|
||||
}
|
||||
}
|
||||
|
||||
VulkanHppGenerator::TypeInfo VulkanHppGenerator::readTypeInfo( tinyxml2::XMLElement const * element ) const
|
||||
TypeInfo VulkanHppGenerator::readTypeInfo( tinyxml2::XMLElement const * element ) const
|
||||
{
|
||||
TypeInfo typeInfo;
|
||||
tinyxml2::XMLNode const * previousSibling = element->PreviousSibling();
|
||||
@ -14807,24 +14793,6 @@ std::vector<std::string> VulkanHppGenerator::selectCommandsByHandle( std::vector
|
||||
return selectedCommands;
|
||||
}
|
||||
|
||||
void VulkanHppGenerator::setVulkanLicenseHeader( int line, std::string const & comment )
|
||||
{
|
||||
checkForError( m_vulkanLicenseHeader.empty(), line, "second encounter of a Copyright comment" );
|
||||
m_vulkanLicenseHeader = comment;
|
||||
|
||||
// replace any '\n' with "\n// "
|
||||
for ( size_t pos = m_vulkanLicenseHeader.find( '\n' ); pos != std::string::npos; pos = m_vulkanLicenseHeader.find( '\n', pos + 1 ) )
|
||||
{
|
||||
m_vulkanLicenseHeader.replace( pos, 1, "\n// " );
|
||||
}
|
||||
// remove any trailing spaces
|
||||
m_vulkanLicenseHeader = trimEnd( m_vulkanLicenseHeader );
|
||||
|
||||
// and add a little message on our own
|
||||
m_vulkanLicenseHeader += "\n\n// This header is generated from the Khronos Vulkan XML API Registry.";
|
||||
m_vulkanLicenseHeader = trim( m_vulkanLicenseHeader ) + "\n";
|
||||
}
|
||||
|
||||
bool VulkanHppGenerator::skipLeadingGrandParent( std::pair<std::string, HandleData> const & handle ) const
|
||||
{
|
||||
bool skip = false;
|
||||
@ -14926,107 +14894,10 @@ void VulkanHppGenerator::EnumData::addEnumValue(
|
||||
}
|
||||
}
|
||||
|
||||
std::string VulkanHppGenerator::TypeInfo::compose( std::string const & nameSpace ) const
|
||||
{
|
||||
return prefix + ( prefix.empty() ? "" : " " ) +
|
||||
( nameSpace.empty() ? type : ( ( ( type.substr( 0, 2 ) == "Vk" ) ? ( nameSpace + "::" ) : "" ) + stripPrefix( type, "Vk" ) ) ) +
|
||||
( postfix.empty() ? "" : " " ) + postfix;
|
||||
}
|
||||
|
||||
//
|
||||
// VulkanHppGenerator local functions
|
||||
//
|
||||
|
||||
// check the validity of an attributes map
|
||||
// line : the line in the xml file where the attributes are listed
|
||||
// attributes : the map of name/value pairs of the encountered attributes
|
||||
// required : the required attributes, with a set of allowed values per attribute
|
||||
// optional : the optional attributes, with a set of allowed values per attribute
|
||||
void checkAttributes( int line,
|
||||
std::map<std::string, std::string> const & attributes,
|
||||
std::map<std::string, std::set<std::string>> const & required,
|
||||
std::map<std::string, std::set<std::string>> const & optional )
|
||||
{
|
||||
// check if all required attributes are included and if there is a set of allowed values, check if the actual
|
||||
// value is part of that set
|
||||
for ( auto const & r : required )
|
||||
{
|
||||
auto attributesIt = attributes.find( r.first );
|
||||
checkForError( attributesIt != attributes.end(), line, "missing attribute <" + r.first + ">" );
|
||||
if ( !r.second.empty() )
|
||||
{
|
||||
std::vector<std::string> values = tokenize( attributesIt->second, "," );
|
||||
for ( auto const & v : values )
|
||||
{
|
||||
checkForError( r.second.find( v ) != r.second.end(), line, "unexpected attribute value <" + v + "> in attribute <" + attributesIt->first + ">" );
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if all not required attributes or optional, and if there is a set of allowed values, check if the
|
||||
// actual value is part of that set
|
||||
for ( auto const & a : attributes )
|
||||
{
|
||||
if ( required.find( a.first ) == required.end() )
|
||||
{
|
||||
auto optionalIt = optional.find( a.first );
|
||||
if ( optionalIt == optional.end() )
|
||||
{
|
||||
checkForWarning( false, line, "unknown attribute <" + a.first + ">" );
|
||||
continue;
|
||||
}
|
||||
if ( !optionalIt->second.empty() )
|
||||
{
|
||||
std::vector<std::string> values = tokenize( a.second, "," );
|
||||
for ( auto const & v : values )
|
||||
{
|
||||
checkForWarning(
|
||||
optionalIt->second.find( v ) != optionalIt->second.end(), line, "unexpected attribute value <" + v + "> in attribute <" + a.first + ">" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkElements( int line,
|
||||
std::vector<tinyxml2::XMLElement const *> const & elements,
|
||||
std::map<std::string, bool> const & required,
|
||||
std::set<std::string> const & optional )
|
||||
{
|
||||
std::map<std::string, size_t> encountered;
|
||||
for ( auto const & e : elements )
|
||||
{
|
||||
std::string value = e->Value();
|
||||
encountered[value]++;
|
||||
checkForWarning(
|
||||
( required.find( value ) != required.end() ) || ( optional.find( value ) != optional.end() ), e->GetLineNum(), "unknown element <" + value + ">" );
|
||||
}
|
||||
for ( auto const & r : required )
|
||||
{
|
||||
auto encounteredIt = encountered.find( r.first );
|
||||
checkForError( encounteredIt != encountered.end(), line, "missing required element <" + r.first + ">" );
|
||||
// check: r.second (means: required excactly once) => (encouteredIt->second == 1)
|
||||
checkForError( !r.second || ( encounteredIt->second == 1 ),
|
||||
line,
|
||||
"required element <" + r.first + "> is supposed to be listed exactly once, but is listed " + std::to_string( encounteredIt->second ) );
|
||||
}
|
||||
}
|
||||
|
||||
void checkForError( bool condition, int line, std::string const & message )
|
||||
{
|
||||
if ( !condition )
|
||||
{
|
||||
throw std::runtime_error( "VulkanHppGenerator: Spec error on line " + std::to_string( line ) + ": " + message );
|
||||
}
|
||||
}
|
||||
|
||||
void checkForWarning( bool condition, int line, std::string const & message )
|
||||
{
|
||||
if ( !condition )
|
||||
{
|
||||
std::cerr << "VulkanHppGenerator: Spec warning on line " << std::to_string( line ) << ": " << message << "!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, size_t>> filterNumbers( std::vector<std::string> const & names )
|
||||
{
|
||||
std::vector<std::pair<std::string, size_t>> filteredNames;
|
||||
@ -15070,78 +14941,11 @@ std::string generateStandardArray( std::string const & type, std::vector<std::st
|
||||
return arrayString;
|
||||
}
|
||||
|
||||
std::string generateStandardArrayWrapper( std::string const & type, std::vector<std::string> const & sizes )
|
||||
{
|
||||
std::string arrayString = "VULKAN_HPP_NAMESPACE::ArrayWrapper" + std::to_string( sizes.size() ) + "D<" + type;
|
||||
for ( auto const & size : sizes )
|
||||
{
|
||||
arrayString += ", " + size;
|
||||
}
|
||||
arrayString += ">";
|
||||
return arrayString;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> getAttributes( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
std::map<std::string, std::string> attributes;
|
||||
for ( auto attribute = element->FirstAttribute(); attribute; attribute = attribute->Next() )
|
||||
{
|
||||
assert( attributes.find( attribute->Name() ) == attributes.end() );
|
||||
attributes[attribute->Name()] = attribute->Value();
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
template <typename ElementContainer>
|
||||
std::vector<tinyxml2::XMLElement const *> getChildElements( ElementContainer const * element )
|
||||
{
|
||||
std::vector<tinyxml2::XMLElement const *> childElements;
|
||||
for ( tinyxml2::XMLElement const * childElement = element->FirstChildElement(); childElement; childElement = childElement->NextSiblingElement() )
|
||||
{
|
||||
childElements.push_back( childElement );
|
||||
}
|
||||
return childElements;
|
||||
}
|
||||
|
||||
bool isNumber( std::string const & name )
|
||||
{
|
||||
return name.find_first_not_of( "0123456789" ) == std::string::npos;
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::string>, std::string> readModifiers( tinyxml2::XMLNode const * node )
|
||||
{
|
||||
std::vector<std::string> arraySizes;
|
||||
std::string bitCount;
|
||||
if ( node && node->ToText() )
|
||||
{
|
||||
// following the name there might be some array size
|
||||
std::string value = node->Value();
|
||||
assert( !value.empty() );
|
||||
if ( value[0] == '[' )
|
||||
{
|
||||
std::string::size_type endPos = 0;
|
||||
while ( endPos + 1 != value.length() )
|
||||
{
|
||||
std::string::size_type startPos = value.find( '[', endPos );
|
||||
checkForError( startPos != std::string::npos, node->GetLineNum(), "could not find '[' in <" + value + ">" );
|
||||
endPos = value.find( ']', startPos );
|
||||
checkForError( endPos != std::string::npos, node->GetLineNum(), "could not find ']' in <" + value + ">" );
|
||||
checkForError( startPos + 2 <= endPos, node->GetLineNum(), "missing content between '[' and ']' in <" + value + ">" );
|
||||
arraySizes.push_back( value.substr( startPos + 1, endPos - startPos - 1 ) );
|
||||
}
|
||||
}
|
||||
else if ( value[0] == ':' )
|
||||
{
|
||||
bitCount = value.substr( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
checkForError( ( value[0] == ';' ) || ( value[0] == ')' ), node->GetLineNum(), "unknown modifier <" + value + ">" );
|
||||
}
|
||||
}
|
||||
return std::make_pair( arraySizes, bitCount );
|
||||
}
|
||||
|
||||
std::string readSnippet( std::string const & snippetFile )
|
||||
{
|
||||
std::ifstream ifs( std::string( BASE_PATH ) + "/snippets/" + snippetFile );
|
||||
@ -15151,57 +14955,6 @@ std::string readSnippet( std::string const & snippetFile )
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string replaceWithMap( std::string const & input, std::map<std::string, std::string> replacements )
|
||||
{
|
||||
// This will match ${someVariable} and contain someVariable in match group 1
|
||||
std::regex re( R"(\$\{([^\}]+)\})" );
|
||||
auto it = std::sregex_iterator( input.begin(), input.end(), re );
|
||||
auto end = std::sregex_iterator();
|
||||
|
||||
// No match, just return the original string
|
||||
if ( it == end )
|
||||
{
|
||||
assert( replacements.empty() );
|
||||
return input;
|
||||
}
|
||||
|
||||
#if !defined( NDEBUG )
|
||||
std::set<std::string> matchedReplacements;
|
||||
#endif
|
||||
|
||||
std::string result = "";
|
||||
while ( it != end )
|
||||
{
|
||||
std::smatch match = *it;
|
||||
auto itReplacement = replacements.find( match[1].str() );
|
||||
assert( itReplacement != replacements.end() );
|
||||
#if !defined( NDEBUG )
|
||||
matchedReplacements.insert( match[1].str() );
|
||||
#endif
|
||||
|
||||
result += match.prefix().str() + ( ( itReplacement != replacements.end() ) ? itReplacement->second : match[0].str() );
|
||||
++it;
|
||||
|
||||
// we've passed the last match. Append the rest of the orignal string
|
||||
if ( it == end )
|
||||
{
|
||||
result += match.suffix().str();
|
||||
}
|
||||
}
|
||||
#if !defined( NDEBUG )
|
||||
std::set<std::string> missedReplacements;
|
||||
for ( auto r : replacements )
|
||||
{
|
||||
if ( matchedReplacements.find( r.first ) == matchedReplacements.end() )
|
||||
{
|
||||
missedReplacements.insert( r.first );
|
||||
}
|
||||
}
|
||||
assert( missedReplacements.empty() );
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string startLowerCase( std::string const & input )
|
||||
{
|
||||
assert( !input.empty() );
|
||||
@ -15214,93 +14967,6 @@ std::string startUpperCase( std::string const & input )
|
||||
return static_cast<char>( toupper( input[0] ) ) + input.substr( 1 );
|
||||
}
|
||||
|
||||
std::string stripPostfix( std::string const & value, std::string const & postfix )
|
||||
{
|
||||
std::string strippedValue = value;
|
||||
if ( strippedValue.ends_with( postfix ) )
|
||||
{
|
||||
strippedValue.erase( strippedValue.length() - postfix.length() );
|
||||
}
|
||||
return strippedValue;
|
||||
}
|
||||
|
||||
std::string stripPrefix( std::string const & value, std::string const & prefix )
|
||||
{
|
||||
std::string strippedValue = value;
|
||||
if ( strippedValue.starts_with( prefix ) )
|
||||
{
|
||||
strippedValue.erase( 0, prefix.length() );
|
||||
}
|
||||
return strippedValue;
|
||||
}
|
||||
|
||||
std::string toCamelCase( std::string const & value )
|
||||
{
|
||||
assert( !value.empty() && ( isupper( value[0] ) || isdigit( value[0] ) ) );
|
||||
std::string result;
|
||||
result.reserve( value.size() );
|
||||
bool keepUpper = true;
|
||||
for ( auto c : value )
|
||||
{
|
||||
if ( c == '_' )
|
||||
{
|
||||
keepUpper = true;
|
||||
}
|
||||
else if ( isdigit( c ) )
|
||||
{
|
||||
keepUpper = true;
|
||||
result.push_back( c );
|
||||
}
|
||||
else if ( keepUpper )
|
||||
{
|
||||
result.push_back( c );
|
||||
keepUpper = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back( static_cast<char>( tolower( c ) ) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string toUpperCase( std::string const & name )
|
||||
{
|
||||
std::string convertedName;
|
||||
bool previousIsLowerCase = false;
|
||||
bool previousIsDigit = false;
|
||||
for ( auto c : name )
|
||||
{
|
||||
if ( ( isupper( c ) && ( previousIsLowerCase || previousIsDigit ) ) || ( isdigit( c ) && previousIsLowerCase ) )
|
||||
{
|
||||
convertedName.push_back( '_' );
|
||||
}
|
||||
convertedName.push_back( static_cast<char>( toupper( c ) ) );
|
||||
previousIsLowerCase = !!islower( c );
|
||||
previousIsDigit = !!isdigit( c );
|
||||
}
|
||||
return convertedName;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokenize( std::string const & tokenString, std::string const & separator )
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
if ( !tokenString.empty() )
|
||||
{
|
||||
size_t start = 0, end;
|
||||
do
|
||||
{
|
||||
end = tokenString.find( separator, start );
|
||||
if ( start != end )
|
||||
{
|
||||
tokens.push_back( trim( tokenString.substr( start, end - start ) ) );
|
||||
}
|
||||
start = end + separator.length();
|
||||
} while ( end != std::string::npos );
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokenizeAny( std::string const & tokenString, std::string const & separators )
|
||||
{
|
||||
size_t len = tokenString.length();
|
||||
@ -15321,41 +14987,6 @@ std::vector<std::string> tokenizeAny( std::string const & tokenString, std::stri
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::string trim( std::string const & input )
|
||||
{
|
||||
std::string result = input;
|
||||
result.erase( result.begin(), std::find_if( result.begin(), result.end(), []( char c ) { return !std::isspace( c ); } ) );
|
||||
result.erase( std::find_if( result.rbegin(), result.rend(), []( char c ) { return !std::isspace( c ); } ).base(), result.end() );
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string trimEnd( std::string const & input )
|
||||
{
|
||||
std::string result = input;
|
||||
result.erase( std::find_if( result.rbegin(), result.rend(), []( char c ) { return !std::isspace( c ); } ).base(), result.end() );
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string trimStars( std::string const & input )
|
||||
{
|
||||
std::string result = input;
|
||||
size_t pos = result.find( '*' );
|
||||
while ( pos != std::string::npos )
|
||||
{
|
||||
if ( ( 0 < pos ) && ( result[pos - 1] != ' ' ) && ( result[pos - 1] != '*' ) )
|
||||
{
|
||||
result.insert( pos, 1, ' ' );
|
||||
++pos;
|
||||
}
|
||||
else if ( ( pos < result.length() - 1 ) && ( result[pos + 1] != ' ' ) && ( result[pos + 1] != '*' ) )
|
||||
{
|
||||
result.insert( pos + 1, 1, ' ' );
|
||||
}
|
||||
pos = result.find( '*', pos + 1 );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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
|
||||
MacroData parseMacro( std::vector<std::string> const & completeMacro )
|
||||
@ -15410,50 +15041,6 @@ MacroData parseMacro( std::vector<std::string> const & completeMacro )
|
||||
return {};
|
||||
}
|
||||
|
||||
void writeToFile( std::string const & str, std::string const & fileName )
|
||||
{
|
||||
std::ofstream ofs( fileName );
|
||||
assert( !ofs.fail() );
|
||||
ofs << str;
|
||||
ofs.close();
|
||||
|
||||
#if defined( CLANG_FORMAT_EXECUTABLE )
|
||||
std::cout << "VulkanHppGenerator: Formatting " << fileName << " ..." << std::endl;
|
||||
std::string commandString = "\"" CLANG_FORMAT_EXECUTABLE "\" -i --style=file " + fileName;
|
||||
int ret = std::system( commandString.c_str() );
|
||||
if ( ret != 0 )
|
||||
{
|
||||
std::cout << "VulkanHppGenerator: failed to format file " << fileName << " with error <" << ret << ">\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string toString( tinyxml2::XMLError error )
|
||||
{
|
||||
switch ( error )
|
||||
{
|
||||
case tinyxml2::XML_SUCCESS: return "XML_SUCCESS";
|
||||
case tinyxml2::XML_NO_ATTRIBUTE: return "XML_NO_ATTRIBUTE";
|
||||
case tinyxml2::XML_WRONG_ATTRIBUTE_TYPE: return "XML_WRONG_ATTRIBUTE_TYPE";
|
||||
case tinyxml2::XML_ERROR_FILE_NOT_FOUND: return "XML_ERROR_FILE_NOT_FOUND";
|
||||
case tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED: return "XML_ERROR_FILE_COULD_NOT_BE_OPENED";
|
||||
case tinyxml2::XML_ERROR_FILE_READ_ERROR: return "XML_ERROR_FILE_READ_ERROR";
|
||||
case tinyxml2::XML_ERROR_PARSING_ELEMENT: return "XML_ERROR_PARSING_ELEMENT";
|
||||
case tinyxml2::XML_ERROR_PARSING_ATTRIBUTE: return "XML_ERROR_PARSING_ATTRIBUTE";
|
||||
case tinyxml2::XML_ERROR_PARSING_TEXT: return "XML_ERROR_PARSING_TEXT";
|
||||
case tinyxml2::XML_ERROR_PARSING_CDATA: return "XML_ERROR_PARSING_CDATA";
|
||||
case tinyxml2::XML_ERROR_PARSING_COMMENT: return "XML_ERROR_PARSING_COMMENT";
|
||||
case tinyxml2::XML_ERROR_PARSING_DECLARATION: return "XML_ERROR_PARSING_DECLARATION";
|
||||
case tinyxml2::XML_ERROR_PARSING_UNKNOWN: return "XML_ERROR_PARSING_UNKNOWN";
|
||||
case tinyxml2::XML_ERROR_EMPTY_DOCUMENT: return "XML_ERROR_EMPTY_DOCUMENT";
|
||||
case tinyxml2::XML_ERROR_MISMATCHED_ELEMENT: return "XML_ERROR_MISMATCHED_ELEMENT";
|
||||
case tinyxml2::XML_ERROR_PARSING: return "XML_ERROR_PARSING";
|
||||
case tinyxml2::XML_CAN_NOT_CONVERT_TEXT: return "XML_CAN_NOT_CONVERT_TEXT";
|
||||
case tinyxml2::XML_NO_TEXT_NODE: return "XML_NO_TEXT_NODE";
|
||||
default: return "unknown error code <" + std::to_string( error ) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
if ( ( argc % 2 ) == 0 )
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "XMLHelper.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -94,45 +96,6 @@ public:
|
||||
void prepareVulkanFuncs();
|
||||
|
||||
private:
|
||||
struct TypeInfo
|
||||
{
|
||||
std::string compose( std::string const & nameSpace ) const;
|
||||
|
||||
bool operator==( TypeInfo const & rhs ) const
|
||||
{
|
||||
return ( prefix == rhs.prefix ) && ( type == rhs.type ) && ( postfix == rhs.postfix );
|
||||
}
|
||||
|
||||
bool operator!=( TypeInfo const & rhs ) const
|
||||
{
|
||||
return !operator==( rhs );
|
||||
}
|
||||
|
||||
bool operator<( TypeInfo const & rhs ) const
|
||||
{
|
||||
return ( prefix < rhs.prefix ) || ( ( prefix == rhs.prefix ) && ( ( type < rhs.type ) || ( ( type == rhs.type ) && ( postfix < rhs.postfix ) ) ) );
|
||||
}
|
||||
|
||||
bool isConstPointer() const
|
||||
{
|
||||
return ( prefix.find( "const" ) != std::string::npos ) && ( postfix.find( '*' ) != std::string::npos );
|
||||
}
|
||||
|
||||
bool isNonConstPointer() const
|
||||
{
|
||||
return ( prefix.find( "const" ) == std::string::npos ) && ( postfix.find( '*' ) != std::string::npos );
|
||||
}
|
||||
|
||||
bool isValue() const
|
||||
{
|
||||
return ( ( prefix.find( '*' ) == std::string::npos ) && ( postfix.find( '*' ) == std::string::npos ) );
|
||||
}
|
||||
|
||||
std::string prefix = {};
|
||||
std::string type = {};
|
||||
std::string postfix = {};
|
||||
};
|
||||
|
||||
struct AliasData
|
||||
{
|
||||
std::string name = {};
|
||||
@ -152,6 +115,29 @@ private:
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct EnumValueData
|
||||
{
|
||||
std::string alias = {};
|
||||
std::string bitpos = {};
|
||||
std::string name = {};
|
||||
std::string protect = {};
|
||||
std::string value = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct EnumData
|
||||
{
|
||||
void addEnumAlias( int line, std::string const & name, std::string const & alias, std::string const & protect, bool supported );
|
||||
void addEnumValue(
|
||||
int line, std::string const & valueName, std::string const & protect, std::string const & bitpos, std::string const & value, bool supported );
|
||||
|
||||
std::string bitwidth = {};
|
||||
bool isBitmask = false;
|
||||
std::vector<EnumValueData> unsupportedValues = {};
|
||||
std::vector<EnumValueData> values = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct NameData
|
||||
{
|
||||
std::string name = {};
|
||||
@ -206,29 +192,6 @@ private:
|
||||
std::map<std::string, DefineData> values = {};
|
||||
};
|
||||
|
||||
struct EnumValueData
|
||||
{
|
||||
std::string alias = {};
|
||||
std::string bitpos = {};
|
||||
std::string name = {};
|
||||
std::string protect = {};
|
||||
std::string value = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct EnumData
|
||||
{
|
||||
void addEnumAlias( int line, std::string const & name, std::string const & alias, std::string const & protect, bool supported );
|
||||
void addEnumValue(
|
||||
int line, std::string const & valueName, std::string const & protect, std::string const & bitpos, std::string const & value, bool supported );
|
||||
|
||||
std::string bitwidth = {};
|
||||
bool isBitmask = false;
|
||||
std::vector<EnumValueData> unsupportedValues = {};
|
||||
std::vector<EnumValueData> values = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct RemoveData
|
||||
{
|
||||
std::vector<std::string> commands = {};
|
||||
@ -245,15 +208,6 @@ private:
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct FeatureData
|
||||
{
|
||||
std::string name = {};
|
||||
std::string number = {};
|
||||
std::vector<RemoveData> removeData = {};
|
||||
std::vector<RequireData> requireData = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct ExtensionData
|
||||
{
|
||||
std::string deprecatedBy = {};
|
||||
@ -269,6 +223,15 @@ private:
|
||||
int xmlLine = 0;
|
||||
};
|
||||
|
||||
struct FeatureData
|
||||
{
|
||||
std::string name = {};
|
||||
std::string number = {};
|
||||
std::vector<RemoveData> removeData = {};
|
||||
std::vector<RequireData> requireData = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct ExternalTypeData
|
||||
{
|
||||
std::string require = {};
|
||||
@ -339,9 +302,10 @@ private:
|
||||
std::vector<std::map<std::string, CommandData>::const_iterator> constructorIts = {};
|
||||
};
|
||||
|
||||
struct IncludeData
|
||||
struct PlatformData
|
||||
{
|
||||
int xmlLine = {};
|
||||
std::string protect = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct MemberData
|
||||
@ -357,16 +321,9 @@ private:
|
||||
std::vector<std::string> selection = {};
|
||||
std::string selector = {};
|
||||
std::string value = {};
|
||||
std::string usedConstant = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct PlatformData
|
||||
{
|
||||
std::string protect = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct StructureData
|
||||
{
|
||||
bool allowDuplicate = {};
|
||||
@ -384,29 +341,6 @@ private:
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
enum class TypeCategory
|
||||
{
|
||||
Bitmask,
|
||||
BaseType,
|
||||
Constant,
|
||||
Define,
|
||||
Enum,
|
||||
ExternalType,
|
||||
FuncPointer,
|
||||
Handle,
|
||||
Include,
|
||||
Struct,
|
||||
Union,
|
||||
Unknown
|
||||
};
|
||||
|
||||
struct TypeData
|
||||
{
|
||||
TypeCategory category = TypeCategory::Unknown;
|
||||
std::set<std::string> requiredBy = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
struct VectorParamData
|
||||
{
|
||||
size_t lenParam = INVALID_INDEX;
|
||||
@ -986,7 +920,6 @@ private:
|
||||
std::pair<bool, ParamData> readCommandParam( tinyxml2::XMLElement const * element, std::vector<ParamData> const & params );
|
||||
std::pair<std::string, std::string> readCommandProto( tinyxml2::XMLElement const * element );
|
||||
void readCommands( tinyxml2::XMLElement const * element );
|
||||
std::string readComment( tinyxml2::XMLElement const * element );
|
||||
void readEnums( tinyxml2::XMLElement const * element );
|
||||
void readEnumsConstants( tinyxml2::XMLElement const * element );
|
||||
void readEnumsEnum( tinyxml2::XMLElement const * element, std::map<std::string, EnumData>::iterator enumIt );
|
||||
@ -1052,7 +985,6 @@ private:
|
||||
std::vector<std::string> selectCommandsByHandle( std::vector<RequireData> const & requireData,
|
||||
std::set<std::string> const & handleCommands,
|
||||
std::set<std::string> & listedCommands ) const;
|
||||
void setVulkanLicenseHeader( int line, std::string const & comment );
|
||||
bool skipLeadingGrandParent( std::pair<std::string, HandleData> const & handle ) const;
|
||||
std::string stripPluralS( std::string const & name ) const;
|
||||
std::string toString( TypeCategory category );
|
||||
|
567
XMLHelper.hpp
Normal file
567
XMLHelper.hpp
Normal file
@ -0,0 +1,567 @@
|
||||
// Copyright(c) 2023, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tinyxml2.h>
|
||||
#include <vector>
|
||||
|
||||
struct TypeInfo;
|
||||
|
||||
void checkAttributes( int line,
|
||||
std::map<std::string, std::string> const & attributes,
|
||||
std::map<std::string, std::set<std::string>> const & required,
|
||||
std::map<std::string, std::set<std::string>> const & optional );
|
||||
void checkElements( int line,
|
||||
std::vector<tinyxml2::XMLElement const *> const & elements,
|
||||
std::map<std::string, bool> const & required,
|
||||
std::set<std::string> const & optional = {} );
|
||||
void checkForError( bool condition, int line, std::string const & message );
|
||||
void checkForWarning( bool condition, int line, std::string const & message );
|
||||
std::string generateCopyrightMessage( std::string const & comment );
|
||||
std::string generateStandardArrayWrapper( std::string const & type, std::vector<std::string> const & sizes );
|
||||
std::map<std::string, std::string> getAttributes( tinyxml2::XMLElement const * element );
|
||||
template <typename ElementContainer>
|
||||
std::vector<tinyxml2::XMLElement const *> getChildElements( ElementContainer const * element );
|
||||
std::string readComment( tinyxml2::XMLElement const * element );
|
||||
std::pair<std::vector<std::string>, std::string> readModifiers( tinyxml2::XMLNode const * node );
|
||||
TypeInfo readTypeInfo( tinyxml2::XMLElement const * element );
|
||||
std::string replaceWithMap( std::string const & input, std::map<std::string, std::string> replacements );
|
||||
std::string stripPostfix( std::string const & value, std::string const & postfix );
|
||||
std::string stripPrefix( std::string const & value, std::string const & prefix );
|
||||
std::string toCamelCase( std::string const & value, bool keepSeparatedNumbersSeparated = false );
|
||||
std::vector<std::string> tokenize( std::string const & tokenString, std::string const & separator );
|
||||
std::string toString( tinyxml2::XMLError error );
|
||||
std::string toUpperCase( std::string const & name );
|
||||
std::string trim( std::string const & input );
|
||||
std::string trimEnd( std::string const & input );
|
||||
std::string trimStars( std::string const & input );
|
||||
void writeToFile( std::string const & str, std::string const & fileName );
|
||||
|
||||
struct TypeInfo
|
||||
{
|
||||
std::string compose( std::string const & nameSpace, std::string const & prefix_ = "Vk" ) const
|
||||
{
|
||||
return prefix + ( prefix.empty() ? "" : " " ) +
|
||||
( nameSpace.empty() ? type : ( ( ( type.starts_with( prefix_ ) ) ? ( nameSpace + "::" ) : "" ) + stripPrefix( type, prefix_ ) ) ) +
|
||||
( postfix.empty() ? "" : " " ) + postfix;
|
||||
}
|
||||
|
||||
bool operator==( TypeInfo const & rhs ) const
|
||||
{
|
||||
return ( prefix == rhs.prefix ) && ( type == rhs.type ) && ( postfix == rhs.postfix );
|
||||
}
|
||||
|
||||
bool operator!=( TypeInfo const & rhs ) const
|
||||
{
|
||||
return !operator==( rhs );
|
||||
}
|
||||
|
||||
bool operator<( TypeInfo const & rhs ) const
|
||||
{
|
||||
return ( prefix < rhs.prefix ) || ( ( prefix == rhs.prefix ) && ( ( type < rhs.type ) || ( ( type == rhs.type ) && ( postfix < rhs.postfix ) ) ) );
|
||||
}
|
||||
|
||||
bool isConstPointer() const
|
||||
{
|
||||
return ( prefix.find( "const" ) != std::string::npos ) && ( postfix.find( '*' ) != std::string::npos );
|
||||
}
|
||||
|
||||
bool isNonConstPointer() const
|
||||
{
|
||||
return ( prefix.find( "const" ) == std::string::npos ) && ( postfix.find( '*' ) != std::string::npos );
|
||||
}
|
||||
|
||||
bool isValue() const
|
||||
{
|
||||
return ( ( prefix.find( '*' ) == std::string::npos ) && ( postfix.find( '*' ) == std::string::npos ) );
|
||||
}
|
||||
|
||||
std::string prefix = {};
|
||||
std::string type = {};
|
||||
std::string postfix = {};
|
||||
};
|
||||
|
||||
struct ExternalTypeData
|
||||
{
|
||||
std::string require = {};
|
||||
int xmlLine = 0;
|
||||
};
|
||||
|
||||
struct IncludeData
|
||||
{
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
enum class TypeCategory
|
||||
{
|
||||
Bitmask,
|
||||
BaseType,
|
||||
Constant,
|
||||
Define,
|
||||
Enum,
|
||||
ExternalType,
|
||||
FuncPointer,
|
||||
Handle,
|
||||
Include,
|
||||
Struct,
|
||||
Union,
|
||||
Unknown
|
||||
};
|
||||
|
||||
struct TypeData
|
||||
{
|
||||
TypeCategory category = TypeCategory::Unknown;
|
||||
std::set<std::string> requiredBy = {};
|
||||
int xmlLine = {};
|
||||
};
|
||||
|
||||
// check the validity of an attributes map
|
||||
// line : the line in the xml file where the attributes are listed
|
||||
// attributes : the map of name/value pairs of the encountered attributes
|
||||
// required : the required attributes, with a set of allowed values per attribute
|
||||
// optional : the optional attributes, with a set of allowed values per attribute
|
||||
inline void checkAttributes( int line,
|
||||
std::map<std::string, std::string> const & attributes,
|
||||
std::map<std::string, std::set<std::string>> const & required,
|
||||
std::map<std::string, std::set<std::string>> const & optional )
|
||||
{
|
||||
// check if all required attributes are included and if there is a set of allowed values, check if the actual
|
||||
// value is part of that set
|
||||
for ( auto const & r : required )
|
||||
{
|
||||
auto attributesIt = attributes.find( r.first );
|
||||
checkForError( attributesIt != attributes.end(), line, "missing attribute <" + r.first + ">" );
|
||||
if ( !r.second.empty() )
|
||||
{
|
||||
std::vector<std::string> values = tokenize( attributesIt->second, "," );
|
||||
for ( auto const & v : values )
|
||||
{
|
||||
checkForError( r.second.find( v ) != r.second.end(), line, "unexpected attribute value <" + v + "> in attribute <" + attributesIt->first + ">" );
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if all not required attributes or optional, and if there is a set of allowed values, check if the
|
||||
// actual value is part of that set
|
||||
for ( auto const & a : attributes )
|
||||
{
|
||||
if ( required.find( a.first ) == required.end() )
|
||||
{
|
||||
auto optionalIt = optional.find( a.first );
|
||||
if ( optionalIt == optional.end() )
|
||||
{
|
||||
checkForWarning( false, line, "unknown attribute <" + a.first + ">" );
|
||||
continue;
|
||||
}
|
||||
if ( !optionalIt->second.empty() )
|
||||
{
|
||||
std::vector<std::string> values = tokenize( a.second, "," );
|
||||
for ( auto const & v : values )
|
||||
{
|
||||
checkForWarning(
|
||||
optionalIt->second.find( v ) != optionalIt->second.end(), line, "unexpected attribute value <" + v + "> in attribute <" + a.first + ">" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void checkElements( int line,
|
||||
std::vector<tinyxml2::XMLElement const *> const & elements,
|
||||
std::map<std::string, bool> const & required,
|
||||
std::set<std::string> const & optional )
|
||||
{
|
||||
std::map<std::string, size_t> encountered;
|
||||
for ( auto const & e : elements )
|
||||
{
|
||||
std::string value = e->Value();
|
||||
encountered[value]++;
|
||||
checkForWarning(
|
||||
( required.find( value ) != required.end() ) || ( optional.find( value ) != optional.end() ), e->GetLineNum(), "unknown element <" + value + ">" );
|
||||
}
|
||||
for ( auto const & r : required )
|
||||
{
|
||||
auto encounteredIt = encountered.find( r.first );
|
||||
checkForError( encounteredIt != encountered.end(), line, "missing required element <" + r.first + ">" );
|
||||
// check: r.second (means: required excactly once) => (encouteredIt->second == 1)
|
||||
checkForError( !r.second || ( encounteredIt->second == 1 ),
|
||||
line,
|
||||
"required element <" + r.first + "> is supposed to be listed exactly once, but is listed " + std::to_string( encounteredIt->second ) );
|
||||
}
|
||||
}
|
||||
|
||||
inline void checkForError( bool condition, int line, std::string const & message )
|
||||
{
|
||||
if ( !condition )
|
||||
{
|
||||
throw std::runtime_error( "VulkanHppGenerator: Spec error on line " + std::to_string( line ) + ": " + message );
|
||||
}
|
||||
}
|
||||
|
||||
inline void checkForWarning( bool condition, int line, std::string const & message )
|
||||
{
|
||||
if ( !condition )
|
||||
{
|
||||
std::cerr << "VulkanHppGenerator: Spec warning on line " << std::to_string( line ) << ": " << message << "!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string generateCopyrightMessage( std::string const & comment )
|
||||
{
|
||||
std::string copyrightMessage = comment;
|
||||
|
||||
// replace any '\n' with "\n// "
|
||||
for ( size_t pos = copyrightMessage.find( '\n' ); pos != std::string::npos; pos = copyrightMessage.find( '\n', pos + 1 ) )
|
||||
{
|
||||
copyrightMessage.replace( pos, 1, "\n// " );
|
||||
}
|
||||
// remove any trailing spaces
|
||||
copyrightMessage = trimEnd( copyrightMessage );
|
||||
|
||||
// and add a little message on our own
|
||||
copyrightMessage += "\n\n// This header is generated from the Khronos Vulkan XML API Registry.";
|
||||
return trim( copyrightMessage ) + "\n";
|
||||
}
|
||||
|
||||
inline std::string generateStandardArrayWrapper( std::string const & type, std::vector<std::string> const & sizes )
|
||||
{
|
||||
std::string arrayString = "VULKAN_HPP_NAMESPACE::ArrayWrapper" + std::to_string( sizes.size() ) + "D<" + type;
|
||||
for ( auto const & size : sizes )
|
||||
{
|
||||
arrayString += ", " + size;
|
||||
}
|
||||
arrayString += ">";
|
||||
return arrayString;
|
||||
}
|
||||
|
||||
inline std::map<std::string, std::string> getAttributes( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
std::map<std::string, std::string> attributes;
|
||||
for ( auto attribute = element->FirstAttribute(); attribute; attribute = attribute->Next() )
|
||||
{
|
||||
assert( attributes.find( attribute->Name() ) == attributes.end() );
|
||||
attributes[attribute->Name()] = attribute->Value();
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
template <typename ElementContainer>
|
||||
inline std::vector<tinyxml2::XMLElement const *> getChildElements( ElementContainer const * element )
|
||||
{
|
||||
std::vector<tinyxml2::XMLElement const *> childElements;
|
||||
for ( tinyxml2::XMLElement const * childElement = element->FirstChildElement(); childElement; childElement = childElement->NextSiblingElement() )
|
||||
{
|
||||
childElements.push_back( childElement );
|
||||
}
|
||||
return childElements;
|
||||
}
|
||||
|
||||
inline std::string readComment( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
int line = element->GetLineNum();
|
||||
checkAttributes( line, getAttributes( element ), {}, {} );
|
||||
checkElements( line, getChildElements( element ), {} );
|
||||
return element->GetText();
|
||||
}
|
||||
|
||||
inline std::pair<std::vector<std::string>, std::string> readModifiers( tinyxml2::XMLNode const * node )
|
||||
{
|
||||
std::vector<std::string> arraySizes;
|
||||
std::string bitCount;
|
||||
if ( node && node->ToText() )
|
||||
{
|
||||
// following the name there might be some array size
|
||||
std::string value = trim( node->Value() );
|
||||
assert( !value.empty() );
|
||||
if ( value[0] == '[' )
|
||||
{
|
||||
std::string::size_type endPos = 0;
|
||||
while ( endPos + 1 != value.length() )
|
||||
{
|
||||
std::string::size_type startPos = value.find( '[', endPos );
|
||||
checkForError( startPos != std::string::npos, node->GetLineNum(), "could not find '[' in <" + value + ">" );
|
||||
endPos = value.find( ']', startPos );
|
||||
checkForError( endPos != std::string::npos, node->GetLineNum(), "could not find ']' in <" + value + ">" );
|
||||
checkForError( startPos + 2 <= endPos, node->GetLineNum(), "missing content between '[' and ']' in <" + value + ">" );
|
||||
arraySizes.push_back( value.substr( startPos + 1, endPos - startPos - 1 ) );
|
||||
}
|
||||
}
|
||||
else if ( value[0] == ':' )
|
||||
{
|
||||
bitCount = trim( value.substr( 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
checkForError( ( value[0] == ';' ) || ( value[0] == ')' ), node->GetLineNum(), "unknown modifier <" + value + ">" );
|
||||
}
|
||||
}
|
||||
return std::make_pair( arraySizes, bitCount );
|
||||
}
|
||||
|
||||
inline TypeInfo readTypeInfo( tinyxml2::XMLElement const * element )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
inline std::string replaceWithMap( std::string const & input, std::map<std::string, std::string> replacements )
|
||||
{
|
||||
// This will match ${someVariable} and contain someVariable in match group 1
|
||||
std::regex re( R"(\$\{([^\}]+)\})" );
|
||||
auto it = std::sregex_iterator( input.begin(), input.end(), re );
|
||||
auto end = std::sregex_iterator();
|
||||
|
||||
// No match, just return the original string
|
||||
if ( it == end )
|
||||
{
|
||||
assert( replacements.empty() );
|
||||
return input;
|
||||
}
|
||||
|
||||
#if !defined( NDEBUG )
|
||||
std::set<std::string> matchedReplacements;
|
||||
#endif
|
||||
|
||||
std::string result = "";
|
||||
while ( it != end )
|
||||
{
|
||||
std::smatch match = *it;
|
||||
auto itReplacement = replacements.find( match[1].str() );
|
||||
assert( itReplacement != replacements.end() );
|
||||
#if !defined( NDEBUG )
|
||||
matchedReplacements.insert( match[1].str() );
|
||||
#endif
|
||||
|
||||
result += match.prefix().str() + ( ( itReplacement != replacements.end() ) ? itReplacement->second : match[0].str() );
|
||||
++it;
|
||||
|
||||
// we've passed the last match. Append the rest of the orignal string
|
||||
if ( it == end )
|
||||
{
|
||||
result += match.suffix().str();
|
||||
}
|
||||
}
|
||||
#if !defined( NDEBUG )
|
||||
std::set<std::string> missedReplacements;
|
||||
for ( auto r : replacements )
|
||||
{
|
||||
if ( matchedReplacements.find( r.first ) == matchedReplacements.end() )
|
||||
{
|
||||
missedReplacements.insert( r.first );
|
||||
}
|
||||
}
|
||||
assert( missedReplacements.empty() );
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string stripPostfix( std::string const & value, std::string const & postfix )
|
||||
{
|
||||
std::string strippedValue = value;
|
||||
if ( strippedValue.ends_with( postfix ) )
|
||||
{
|
||||
strippedValue.erase( strippedValue.length() - postfix.length() );
|
||||
}
|
||||
return strippedValue;
|
||||
}
|
||||
|
||||
inline std::string stripPrefix( std::string const & value, std::string const & prefix )
|
||||
{
|
||||
std::string strippedValue = value;
|
||||
if ( strippedValue.starts_with( prefix ) )
|
||||
{
|
||||
strippedValue.erase( 0, prefix.length() );
|
||||
}
|
||||
return strippedValue;
|
||||
}
|
||||
|
||||
inline std::string toCamelCase( std::string const & value, bool keepSeparatedNumbersSeparated )
|
||||
{
|
||||
assert( !value.empty() && ( isupper( value[0] ) || isdigit( value[0] ) ) );
|
||||
std::string result;
|
||||
result.reserve( value.size() );
|
||||
for ( size_t i = 0; i < value.size(); ++i )
|
||||
{
|
||||
if ( value[i] == '_' )
|
||||
{
|
||||
if ( keepSeparatedNumbersSeparated && ( 0 < i ) && isdigit( value[i - 1] ) && ( i < value.size() - 1 ) && isdigit( value[i + 1] ) )
|
||||
{
|
||||
result.push_back( '_' );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back( ( ( 0 == i ) || ( value[i - 1] == '_' ) || isdigit( value[i - 1] ) ) ? value[i] : static_cast<char>( tolower( value[i] ) ) );
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
bool keepUpper = true;
|
||||
for ( auto c : value )
|
||||
{
|
||||
if ( c == '_' )
|
||||
{
|
||||
keepUpper = true;
|
||||
}
|
||||
else if ( isdigit( c ) )
|
||||
{
|
||||
keepUpper = true;
|
||||
result.push_back( c );
|
||||
}
|
||||
else if ( keepUpper )
|
||||
{
|
||||
result.push_back( c );
|
||||
keepUpper = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back( static_cast<char>( tolower( c ) ) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::vector<std::string> tokenize( std::string const & tokenString, std::string const & separator )
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
if ( !tokenString.empty() )
|
||||
{
|
||||
size_t start = 0, end;
|
||||
do
|
||||
{
|
||||
end = tokenString.find( separator, start );
|
||||
if ( start != end )
|
||||
{
|
||||
tokens.push_back( trim( tokenString.substr( start, end - start ) ) );
|
||||
}
|
||||
start = end + separator.length();
|
||||
} while ( end != std::string::npos );
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
inline std::string toString( tinyxml2::XMLError error )
|
||||
{
|
||||
switch ( error )
|
||||
{
|
||||
case tinyxml2::XML_SUCCESS: return "XML_SUCCESS";
|
||||
case tinyxml2::XML_NO_ATTRIBUTE: return "XML_NO_ATTRIBUTE";
|
||||
case tinyxml2::XML_WRONG_ATTRIBUTE_TYPE: return "XML_WRONG_ATTRIBUTE_TYPE";
|
||||
case tinyxml2::XML_ERROR_FILE_NOT_FOUND: return "XML_ERROR_FILE_NOT_FOUND";
|
||||
case tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED: return "XML_ERROR_FILE_COULD_NOT_BE_OPENED";
|
||||
case tinyxml2::XML_ERROR_FILE_READ_ERROR: return "XML_ERROR_FILE_READ_ERROR";
|
||||
case tinyxml2::XML_ERROR_PARSING_ELEMENT: return "XML_ERROR_PARSING_ELEMENT";
|
||||
case tinyxml2::XML_ERROR_PARSING_ATTRIBUTE: return "XML_ERROR_PARSING_ATTRIBUTE";
|
||||
case tinyxml2::XML_ERROR_PARSING_TEXT: return "XML_ERROR_PARSING_TEXT";
|
||||
case tinyxml2::XML_ERROR_PARSING_CDATA: return "XML_ERROR_PARSING_CDATA";
|
||||
case tinyxml2::XML_ERROR_PARSING_COMMENT: return "XML_ERROR_PARSING_COMMENT";
|
||||
case tinyxml2::XML_ERROR_PARSING_DECLARATION: return "XML_ERROR_PARSING_DECLARATION";
|
||||
case tinyxml2::XML_ERROR_PARSING_UNKNOWN: return "XML_ERROR_PARSING_UNKNOWN";
|
||||
case tinyxml2::XML_ERROR_EMPTY_DOCUMENT: return "XML_ERROR_EMPTY_DOCUMENT";
|
||||
case tinyxml2::XML_ERROR_MISMATCHED_ELEMENT: return "XML_ERROR_MISMATCHED_ELEMENT";
|
||||
case tinyxml2::XML_ERROR_PARSING: return "XML_ERROR_PARSING";
|
||||
case tinyxml2::XML_CAN_NOT_CONVERT_TEXT: return "XML_CAN_NOT_CONVERT_TEXT";
|
||||
case tinyxml2::XML_NO_TEXT_NODE: return "XML_NO_TEXT_NODE";
|
||||
default: return "unknown error code <" + std::to_string( error ) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
std::string toUpperCase( std::string const & name )
|
||||
{
|
||||
std::string convertedName;
|
||||
bool previousIsLowerCase = false;
|
||||
bool previousIsDigit = false;
|
||||
for ( auto c : name )
|
||||
{
|
||||
if ( ( isupper( c ) && ( previousIsLowerCase || previousIsDigit ) ) || ( isdigit( c ) && previousIsLowerCase ) )
|
||||
{
|
||||
convertedName.push_back( '_' );
|
||||
}
|
||||
convertedName.push_back( static_cast<char>( toupper( c ) ) );
|
||||
previousIsLowerCase = !!islower( c );
|
||||
previousIsDigit = !!isdigit( c );
|
||||
}
|
||||
return convertedName;
|
||||
}
|
||||
|
||||
inline std::string trim( std::string const & input )
|
||||
{
|
||||
std::string result = input;
|
||||
result.erase( result.begin(), std::find_if( result.begin(), result.end(), []( char c ) { return !std::isspace( c ); } ) );
|
||||
result.erase( std::find_if( result.rbegin(), result.rend(), []( char c ) { return !std::isspace( c ); } ).base(), result.end() );
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string trimEnd( std::string const & input )
|
||||
{
|
||||
std::string result = input;
|
||||
result.erase( std::find_if( result.rbegin(), result.rend(), []( char c ) { return !std::isspace( c ); } ).base(), result.end() );
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string trimStars( std::string const & input )
|
||||
{
|
||||
std::string result = input;
|
||||
size_t pos = result.find( '*' );
|
||||
while ( pos != std::string::npos )
|
||||
{
|
||||
if ( ( 0 < pos ) && ( result[pos - 1] != ' ' ) && ( result[pos - 1] != '*' ) )
|
||||
{
|
||||
result.insert( pos, 1, ' ' );
|
||||
++pos;
|
||||
}
|
||||
else if ( ( pos < result.length() - 1 ) && ( result[pos + 1] != ' ' ) && ( result[pos + 1] != '*' ) )
|
||||
{
|
||||
result.insert( pos + 1, 1, ' ' );
|
||||
}
|
||||
pos = result.find( '*', pos + 1 );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void writeToFile( std::string const & str, std::string const & fileName )
|
||||
{
|
||||
std::ofstream ofs( fileName );
|
||||
assert( !ofs.fail() );
|
||||
ofs << str;
|
||||
ofs.close();
|
||||
|
||||
#if defined( CLANG_FORMAT_EXECUTABLE )
|
||||
std::cout << "VulkanHppGenerator: Formatting " << fileName << " ..." << std::endl;
|
||||
std::string commandString = "\"" CLANG_FORMAT_EXECUTABLE "\" -i --style=file " + fileName;
|
||||
int ret = std::system( commandString.c_str() );
|
||||
if ( ret != 0 )
|
||||
{
|
||||
std::cout << "VulkanHppGenerator: failed to format file " << fileName << " with error <" << ret << ">\n";
|
||||
}
|
||||
#endif
|
||||
}
|
2696
vulkan/vulkan_video.hpp
Normal file
2696
vulkan/vulkan_video.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user