// Copyright(c) 2015-2016, 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 #include #include #include #include #include #include #include #include #include "VulkanHppGenerator.hpp" const std::string vkNamespace = R"( #if !defined(VULKAN_HPP_NAMESPACE) #define VULKAN_HPP_NAMESPACE vk #endif #define VULKAN_HPP_STRINGIFY2(text) #text #define VULKAN_HPP_STRINGIFY(text) VULKAN_HPP_STRINGIFY2(text) #define VULKAN_HPP_NAMESPACE_STRING VULKAN_HPP_STRINGIFY(VULKAN_HPP_NAMESPACE) namespace VULKAN_HPP_NAMESPACE { )"; const std::string constExprHeader = R"( #if defined(_MSC_VER) && (_MSC_VER <= 1800) # define VULKAN_HPP_CONSTEXPR #else # define VULKAN_HPP_CONSTEXPR constexpr #endif )"; const std::string exceptionHeader = R"( #if defined(_MSC_VER) && (_MSC_VER == 1800) # define noexcept _NOEXCEPT #endif class ErrorCategoryImpl : public std::error_category { public: virtual const char* name() const noexcept override { return VULKAN_HPP_NAMESPACE_STRING"::Result"; } virtual std::string message(int ev) const override { return to_string(static_cast(ev)); } }; #if defined(_MSC_VER) && (_MSC_VER == 1800) # undef noexcept #endif VULKAN_HPP_INLINE const std::error_category& errorCategory() { static ErrorCategoryImpl instance; return instance; } VULKAN_HPP_INLINE std::error_code make_error_code(Result e) { return std::error_code(static_cast(e), errorCategory()); } VULKAN_HPP_INLINE std::error_condition make_error_condition(Result e) { return std::error_condition(static_cast(e), errorCategory()); } )"; const std::string exceptionClassesHeader = R"( #if defined(_MSC_VER) && (_MSC_VER == 1800) # define noexcept _NOEXCEPT #endif class Error { public: virtual ~Error() = default; virtual const char* what() const noexcept = 0; }; class LogicError : public Error, public std::logic_error { public: explicit LogicError( const std::string& what ) : Error(), std::logic_error(what) {} explicit LogicError( char const * what ) : Error(), std::logic_error(what) {} virtual ~LogicError() = default; virtual const char* what() const noexcept { return std::logic_error::what(); } }; class SystemError : public Error, public std::system_error { public: SystemError( std::error_code ec ) : Error(), std::system_error(ec) {} SystemError( std::error_code ec, std::string const& what ) : Error(), std::system_error(ec, what) {} SystemError( std::error_code ec, char const * what ) : Error(), std::system_error(ec, what) {} SystemError( int ev, std::error_category const& ecat ) : Error(), std::system_error(ev, ecat) {} SystemError( int ev, std::error_category const& ecat, std::string const& what) : Error(), std::system_error(ev, ecat, what) {} SystemError( int ev, std::error_category const& ecat, char const * what) : Error(), std::system_error(ev, ecat, what) {} virtual ~SystemError() = default; virtual const char* what() const noexcept { return std::system_error::what(); } }; #if defined(_MSC_VER) && (_MSC_VER == 1800) # undef noexcept #endif )"; const std::string flagsHeader = R"( template struct FlagTraits { enum { allFlags = 0 }; }; template class Flags { public: VULKAN_HPP_CONSTEXPR Flags() : m_mask(0) { } Flags(BitType bit) : m_mask(static_cast(bit)) { } Flags(Flags const& rhs) : m_mask(rhs.m_mask) { } explicit Flags(MaskType flags) : m_mask(flags) { } Flags & operator=(Flags const& rhs) { m_mask = rhs.m_mask; return *this; } Flags & operator|=(Flags const& rhs) { m_mask |= rhs.m_mask; return *this; } Flags & operator&=(Flags const& rhs) { m_mask &= rhs.m_mask; return *this; } Flags & operator^=(Flags const& rhs) { m_mask ^= rhs.m_mask; return *this; } Flags operator|(Flags const& rhs) const { Flags result(*this); result |= rhs; return result; } Flags operator&(Flags const& rhs) const { Flags result(*this); result &= rhs; return result; } Flags operator^(Flags const& rhs) const { Flags result(*this); result ^= rhs; return result; } bool operator!() const { return !m_mask; } Flags operator~() const { Flags result(*this); result.m_mask ^= FlagTraits::allFlags; return result; } bool operator==(Flags const& rhs) const { return m_mask == rhs.m_mask; } bool operator!=(Flags const& rhs) const { return m_mask != rhs.m_mask; } explicit operator bool() const { return !!m_mask; } explicit operator MaskType() const { return m_mask; } private: MaskType m_mask; }; template Flags operator|(BitType bit, Flags const& flags) { return flags | bit; } template Flags operator&(BitType bit, Flags const& flags) { return flags & bit; } template Flags operator^(BitType bit, Flags const& flags) { return flags ^ bit; } )"; const std::string optionalClassHeader = R"( template class Optional { public: Optional(RefType & reference) { m_ptr = &reference; } Optional(RefType * ptr) { m_ptr = ptr; } Optional(std::nullptr_t) { m_ptr = nullptr; } operator RefType*() const { return m_ptr; } RefType const* operator->() const { return m_ptr; } explicit operator bool() const { return !!m_ptr; } private: RefType *m_ptr; }; )"; const std::string arrayProxyHeader = R"( #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template class ArrayProxy { public: VULKAN_HPP_CONSTEXPR ArrayProxy(std::nullptr_t) : m_count(0) , m_ptr(nullptr) {} ArrayProxy(T & ptr) : m_count(1) , m_ptr(&ptr) {} ArrayProxy(uint32_t count, T * ptr) : m_count(count) , m_ptr(ptr) {} template ArrayProxy(std::array::type, N> & data) : m_count(N) , m_ptr(data.data()) {} template ArrayProxy(std::array::type, N> const& data) : m_count(N) , m_ptr(data.data()) {} template ::type>> ArrayProxy(std::vector::type, Allocator> & data) : m_count(static_cast(data.size())) , m_ptr(data.data()) {} template ::type>> ArrayProxy(std::vector::type, Allocator> const& data) : m_count(static_cast(data.size())) , m_ptr(data.data()) {} ArrayProxy(std::initializer_list const& data) : m_count(static_cast(data.end() - data.begin())) , m_ptr(data.begin()) {} const T * begin() const { return m_ptr; } const T * end() const { return m_ptr + m_count; } const T & front() const { VULKAN_HPP_ASSERT(m_count && m_ptr); return *m_ptr; } const T & back() const { VULKAN_HPP_ASSERT(m_count && m_ptr); return *(m_ptr + m_count - 1); } bool empty() const { return (m_count == 0); } uint32_t size() const { return m_count; } T * data() const { return m_ptr; } private: uint32_t m_count; T * m_ptr; }; #endif )"; const std::string structureChainHeader = R"( template struct isStructureChainValid { enum { value = false }; }; template struct TypeList { using list = P; using last = T; }; template struct extendCheck { static const bool valid = isStructureChainValid::value || extendCheck::valid; }; template struct extendCheck,X> { static const bool valid = isStructureChainValid::value; }; template struct extendCheck { static const bool valid = true; }; template class StructureChainElement { public: explicit operator Element&() { return value; } explicit operator const Element&() const { return value; } private: Element value; }; template class StructureChain : private StructureChainElement... { public: StructureChain() { link(); } StructureChain(StructureChain const &rhs) { linkAndCopy(rhs); } StructureChain(StructureElements const &... elems) { linkAndCopyElements(elems...); } StructureChain& operator=(StructureChain const &rhs) { linkAndCopy(rhs); return *this; } template ClassType& get() { return static_cast(*this);} private: template void link() { static_assert(extendCheck::valid, "The structure chain is not valid!"); } template void link() { static_assert(extendCheck::valid, "The structure chain is not valid!"); X& x = static_cast(*this); Y& y = static_cast(*this); x.pNext = &y; link, Y, Z...>(); } template void linkAndCopy(StructureChain const &rhs) { static_assert(extendCheck::valid, "The structure chain is not valid!"); static_cast(*this) = static_cast(rhs); } template void linkAndCopy(StructureChain const &rhs) { static_assert(extendCheck::valid, "The structure chain is not valid!"); X& x = static_cast(*this); Y& y = static_cast(*this); x = static_cast(rhs); x.pNext = &y; linkAndCopy, Y, Z...>(rhs); } template void linkAndCopyElements(X const &xelem) { static_assert(extendCheck::valid, "The structure chain is not valid!"); static_cast(*this) = xelem; } template void linkAndCopyElements(X const &xelem, Y const &yelem, Z const &... zelem) { static_assert(extendCheck::valid, "The structure chain is not valid!"); X& x = static_cast(*this); Y& y = static_cast(*this); x = xelem; x.pNext = &y; linkAndCopyElements, Y, Z...>(yelem, zelem...); } }; )"; const std::string versionCheckHeader = R"( #if !defined(VULKAN_HPP_HAS_UNRESTRICTED_UNIONS) # if defined(__clang__) # if __has_feature(cxx_unrestricted_unions) # define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS # endif # elif defined(__GNUC__) # define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) # if 40600 <= GCC_VERSION # define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS # endif # elif defined(_MSC_VER) # if 1900 <= _MSC_VER # define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS # endif # endif #endif )"; const std::string inlineHeader = R"( #if !defined(VULKAN_HPP_INLINE) # if defined(__clang___) # if __has_attribute(always_inline) # define VULKAN_HPP_INLINE __attribute__((always_inline)) __inline__ # else # define VULKAN_HPP_INLINE inline # endif # elif defined(__GNUC__) # define VULKAN_HPP_INLINE __attribute__((always_inline)) __inline__ # elif defined(_MSC_VER) # define VULKAN_HPP_INLINE inline # else # define VULKAN_HPP_INLINE inline # endif #endif )"; const std::string explicitHeader = R"( #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) # define VULKAN_HPP_TYPESAFE_EXPLICIT #else # define VULKAN_HPP_TYPESAFE_EXPLICIT explicit #endif )"; const std::string resultValueHeader = R"( template struct ResultValue { ResultValue( Result r, T & v ) : result( r ) , value( v ) {} ResultValue( Result r, T && v ) : result( r ) , value( std::move( v ) ) {} Result result; T value; operator std::tuple() { return std::tuple(result, value); } }; template struct ResultValueType { #ifdef VULKAN_HPP_NO_EXCEPTIONS typedef ResultValue type; #else typedef T type; #endif }; template <> struct ResultValueType { #ifdef VULKAN_HPP_NO_EXCEPTIONS typedef Result type; #else typedef void type; #endif }; )"; const std::string createResultValueHeader = R"( VULKAN_HPP_INLINE ResultValueType::type createResultValue( Result result, char const * message ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_ASSERT( result == Result::eSuccess ); return result; #else if ( result != Result::eSuccess ) { throwResultException( result, message ); } #endif } template VULKAN_HPP_INLINE typename ResultValueType::type createResultValue( Result result, T & data, char const * message ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_ASSERT( result == Result::eSuccess ); return ResultValue( result, data ); #else if ( result != Result::eSuccess ) { throwResultException( result, message ); } return std::move( data ); #endif } VULKAN_HPP_INLINE Result createResultValue( Result result, char const * message, std::initializer_list successCodes ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_ASSERT( std::find( successCodes.begin(), successCodes.end(), result ) != successCodes.end() ); #else if ( std::find( successCodes.begin(), successCodes.end(), result ) == successCodes.end() ) { throwResultException( result, message ); } #endif return result; } template VULKAN_HPP_INLINE ResultValue createResultValue( Result result, T & data, char const * message, std::initializer_list successCodes ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_ASSERT( std::find( successCodes.begin(), successCodes.end(), result ) != successCodes.end() ); #else if ( std::find( successCodes.begin(), successCodes.end(), result ) == successCodes.end() ) { throwResultException( result, message ); } #endif return ResultValue( result, data ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE template VULKAN_HPP_INLINE typename ResultValueType>::type createResultValue( Result result, T & data, char const * message, typename UniqueHandleTraits::deleter const& deleter ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_ASSERT( result == Result::eSuccess ); return ResultValue>( result, UniqueHandle(data, deleter) ); #else if ( result != Result::eSuccess ) { throwResultException( result, message ); } return UniqueHandle(data, deleter); #endif } #endif )"; const std::string uniqueHandleHeader = R"( #ifndef VULKAN_HPP_NO_SMART_HANDLE template class UniqueHandleTraits; template class UniqueHandle : public UniqueHandleTraits::deleter { private: using Deleter = typename UniqueHandleTraits::deleter; public: explicit UniqueHandle( Type const& value = Type(), Deleter const& deleter = Deleter() ) : Deleter( deleter) , m_value( value ) {} UniqueHandle( UniqueHandle const& ) = delete; UniqueHandle( UniqueHandle && other ) : Deleter( std::move( static_cast( other ) ) ) , m_value( other.release() ) {} ~UniqueHandle() { if ( m_value ) this->destroy( m_value ); } UniqueHandle & operator=( UniqueHandle const& ) = delete; UniqueHandle & operator=( UniqueHandle && other ) { reset( other.release() ); *static_cast(this) = std::move( static_cast(other) ); return *this; } explicit operator bool() const { return m_value.operator bool(); } Type const* operator->() const { return &m_value; } Type * operator->() { return &m_value; } Type const& operator*() const { return m_value; } Type & operator*() { return m_value; } const Type & get() const { return m_value; } Type & get() { return m_value; } void reset( Type const& value = Type() ) { if ( m_value != value ) { if ( m_value ) this->destroy( m_value ); m_value = value; } } Type release() { Type value = m_value; m_value = nullptr; return value; } void swap( UniqueHandle & rhs ) { std::swap(m_value, rhs.m_value); std::swap(static_cast(*this), static_cast(rhs)); } private: Type m_value; }; template VULKAN_HPP_INLINE void swap( UniqueHandle & lhs, UniqueHandle & rhs ) { lhs.swap( rhs ); } #endif )"; const std::string deleterClassString = R"( struct AllocationCallbacks; template class ObjectDestroy { public: ObjectDestroy( OwnerType owner = OwnerType(), Optional allocator = nullptr, Dispatch const &dispatch = Dispatch() ) : m_owner( owner ) , m_allocator( allocator ) , m_dispatch( &dispatch ) {} OwnerType getOwner() const { return m_owner; } Optional getAllocator() const { return m_allocator; } protected: template void destroy(T t) { m_owner.destroy( t, m_allocator, *m_dispatch ); } private: OwnerType m_owner; Optional m_allocator; Dispatch const* m_dispatch; }; class NoParent; template class ObjectDestroy { public: ObjectDestroy( Optional allocator = nullptr, Dispatch const &dispatch = Dispatch() ) : m_allocator( allocator ) , m_dispatch( &dispatch ) {} Optional getAllocator() const { return m_allocator; } protected: template void destroy(T t) { t.destroy( m_allocator, *m_dispatch ); } private: Optional m_allocator; Dispatch const* m_dispatch; }; template class ObjectFree { public: ObjectFree( OwnerType owner = OwnerType(), Optional allocator = nullptr, Dispatch const &dispatch = Dispatch() ) : m_owner( owner ) , m_allocator( allocator ) , m_dispatch( &dispatch ) {} OwnerType getOwner() const { return m_owner; } Optional getAllocator() const { return m_allocator; } protected: template void destroy(T t) { m_owner.free( t, m_allocator, *m_dispatch ); } private: OwnerType m_owner; Optional m_allocator; Dispatch const* m_dispatch; }; template class PoolFree { public: PoolFree( OwnerType owner = OwnerType(), PoolType pool = PoolType(), Dispatch const &dispatch = Dispatch() ) : m_owner( owner ) , m_pool( pool ) , m_dispatch( &dispatch ) {} OwnerType getOwner() const { return m_owner; } PoolType getPool() const { return m_pool; } protected: template void destroy(T t) { m_owner.free( m_pool, t, *m_dispatch ); } private: OwnerType m_owner; PoolType m_pool; Dispatch const* m_dispatch; }; )"; std::string replaceWithMap(std::string const &input, std::map 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) { return input; } std::string result = ""; while (it != end) { std::smatch match = *it; auto itReplacement = replacements.find(match[1].str()); assert(itReplacement != replacements.end()); 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(); } } return result; } bool beginsWith(std::string const& text, std::string const& prefix); void checkAttributes(std::map const& attributes, int line, std::map> const& required, std::map> const& optional); void checkElements(std::vector const& elements, std::set const& values); void checkEmptyElement(tinyxml2::XMLElement const* element); void checkOrderedElements(std::vector const& elements, std::vector const& values); std::string createEnumValueName(std::string const& name, std::string const& prefix, std::string const& postfix, bool bitmask, std::string const& tag); bool endsWith(std::string const& text, std::string const& postfix); void enterProtect(std::ostream &os, std::string const& protect); std::string extractTag(std::string const& name); std::string findTag(std::string const& name, std::set const& tags); std::string generateEnumNameForFlags(std::string const& name); std::map getAttributes(tinyxml2::XMLElement const* element); std::vector getChildElements(tinyxml2::XMLElement const* element); bool isErrorEnum(std::string const& enumName); void leaveProtect(std::ostream &os, std::string const& protect); std::string readArraySize(tinyxml2::XMLNode const* node, std::string& name); std::string startUpperCase(std::string const& input); std::string startLowerCase(std::string const& input); std::string strip(std::string const& value, std::string const& prefix, std::string const& postfix = std::string()); std::string stripErrorEnumPrefix(std::string const& enumName); std::string stripPluralS(std::string const& name); std::vector tokenize(std::string tokenString, char separator); std::string trim(std::string const& input); std::string trimEnd(std::string const& input); std::string toCamelCase(std::string const& value); std::string toUpperCase(std::string const& name); void writeFunctionHeaderName(std::ostream & os, std::string const& name, bool singular, bool unique); void writeReinterpretCast(std::ostream & os, bool leadingConst, bool vulkanType, std::string const& type, bool trailingPointerToConst); void writeStandardOrEnhanced(std::ostream & os, std::string const& standard, std::string const& enhanced); void writeTypesafeCheck(std::ostream & os, std::string const& typesafeCheck); void writeVersionCheck(std::ostream & os, std::string const& version); #if !defined(NDEBUG) void skipFeatureRequire(tinyxml2::XMLElement const* element); void skipImplicitExternSyncParams(tinyxml2::XMLElement const* element); void skipTypeEnum(tinyxml2::XMLElement const* element, std::map const& attributes); void skipTypeInclude(tinyxml2::XMLElement const* element); #endif bool beginsWith(std::string const& text, std::string const& prefix) { return !prefix.empty() && text.substr(0, prefix.length()) == prefix; } // check the validity of an attributes map // attributes : the map of name/value pairs of the encountered attributes // line : the line in the xml file where the attributes are listed // 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(std::map const& attributes, int line, std::map> const& required, std::map> const& optional) { std::stringstream ss; ss << line; std::string lineNumber = ss.str(); // 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); if (attributesIt == attributes.end()) { throw std::runtime_error("Spec error on line " + lineNumber + ": missing attribute <" + r.first + ">"); } if (!r.second.empty() && (r.second.find(attributesIt->second) == r.second.end())) { throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected attribute value <" + attributesIt->second + "> in attribute <" + r.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()) { std::cerr << "warning: " << "Unknown attribute " + a.first + " in line " + lineNumber + "!" << std::endl; continue; } if (!optionalIt->second.empty()) { std::vector values = tokenize(a.second, ','); for (auto const& v : values) { if (optionalIt->second.find(v) == optionalIt->second.end()) { throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected attribute value <" + v + "> in attribute <" + a.first + ">"); } } } } } } void checkElements(std::vector const& elements, std::set const& values) { for (auto e : elements) { if (values.find(e->Value()) == values.end()) { std::stringstream ss; ss << e->GetLineNum(); std::string lineNumber = ss.str(); std::cerr << "warning: Unknown element in spec on line: " << lineNumber << " " << e->Value() << "!" << std::endl; } } } void checkEmptyElement(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); checkElements(getChildElements(element), {}); } void checkOrderedElements(std::vector const& elements, std::vector const& values) { for (size_t i = 0; i < elements.size(); i++) { std::stringstream ss; ss << elements[i]->GetLineNum(); std::string lineNumber = ss.str(); if (values.size() <= i) { throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected surplus element <" + elements[i]->Value() + ">"); } if (values[i] != elements[i]->Value()) { throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected element <" + elements[i]->Value() + ">, expected <" + values[i] + ">"); } } } std::string createEnumValueName(std::string const& name, std::string const& prefix, std::string const& postfix, bool bitmask, std::string const& tag) { std::string result = "e" + toCamelCase(strip(name, prefix, postfix)); if (bitmask) { size_t pos = result.find("Bit"); if (pos != std::string::npos) { result.erase(pos, 3); } } if (!tag.empty() && (result.substr(result.length() - tag.length()) == toCamelCase(tag))) { result = result.substr(0, result.length() - tag.length()) + tag; } return result; } bool endsWith(std::string const& text, std::string const& postfix) { return !postfix.empty() && (postfix.length() < text.length()) && (text.substr(text.length() - postfix.length()) == postfix); } void enterProtect(std::ostream &os, std::string const& protect) { if (!protect.empty()) { os << "#ifdef " << protect << std::endl; } } std::string extractTag(std::string const& name) { // the name is supposed to look like: VK__ size_t start = name.find('_'); assert((start != std::string::npos) && (name.substr(0, start) == "VK")); size_t end = name.find('_', start + 1); assert(end != std::string::npos); return name.substr(start + 1, end - start - 1); } std::string findTag(std::string const& name, std::set const& tags) { // find the tag in a name, return that tag or an empty string auto tagIt = std::find_if(tags.begin(), tags.end(), [&name](std::string const& t) { size_t pos = name.find(t); return (pos != std::string::npos) && (pos == name.length() - t.length()); }); return tagIt != tags.end() ? *tagIt : ""; } std::string generateEnumNameForFlags(std::string const& name) { // create a string, where the substring "Flags" is replaced by "FlagBits" std::string generatedName = name; size_t pos = generatedName.rfind("Flags"); assert(pos != std::string::npos); generatedName.replace(pos, 5, "FlagBits"); return generatedName; } std::map getAttributes(tinyxml2::XMLElement const* element) { std::map attributes; for (auto attribute = element->FirstAttribute(); attribute; attribute = attribute->Next()) { assert(attributes.find(attribute->Name()) == attributes.end()); attributes[attribute->Name()] = attribute->Value(); } return attributes; } std::vector getChildElements(tinyxml2::XMLElement const* element) { std::vector childElements; for (tinyxml2::XMLElement const* childElement = element->FirstChildElement(); childElement; childElement = childElement->NextSiblingElement()) { childElements.push_back(childElement); } return childElements; } bool isErrorEnum(std::string const& enumName) { return (enumName.substr(0, 6) == "eError"); } void leaveProtect(std::ostream &os, std::string const& protect) { if (!protect.empty()) { os << "#endif /*" << protect << "*/" << std::endl; } } std::string readArraySize(tinyxml2::XMLNode const* node, std::string& name) { std::string arraySize; if (name.back() == ']') { // if the parameter has '[' and ']' in its name, get the stuff inbetween those as the array size and erase that part from the parameter name assert(!node->NextSibling()); size_t pos = name.find('['); assert(pos != std::string::npos); arraySize = name.substr(pos + 1, name.length() - 2 - pos); name.erase(pos); } else { // otherwise look for a sibling of this node node = node->NextSibling(); if (node && node->ToText()) { assert(node->Value()); std::string value = trimEnd(node->Value()); if (value == "[") { // if this node has '[' as its value, the next node holds the array size, and the node after that needs to hold ']', and there should be no more siblings node = node->NextSibling(); assert(node && node->ToElement() && (strcmp(node->Value(), "enum") == 0)); arraySize = node->ToElement()->GetText(); node = node->NextSibling(); assert(node && node->ToText() && (trimEnd(node->Value()) == "]")); } else { // otherwise, the node holds '[' and ']', so get the stuff in between those as the array size assert((value.front() == '[') && (value.back() == ']')); arraySize = value.substr(1, value.length() - 2); } assert(!node->NextSibling() || ((strcmp(node->NextSibling()->Value(), "comment") == 0) && !node->NextSibling()->NextSibling())); } } return arraySize; } std::string startUpperCase(std::string const& input) { return static_cast(toupper(input[0])) + input.substr(1); } std::string startLowerCase(std::string const& input) { return input.empty() ? "" : static_cast(tolower(input[0])) + input.substr(1); } std::string strip(std::string const& value, std::string const& prefix, std::string const& postfix) { std::string strippedValue = value; if (beginsWith(strippedValue, prefix)) { strippedValue.erase(0, prefix.length()); } if (endsWith(strippedValue, postfix)) { strippedValue.erase(strippedValue.length() - postfix.length()); } return strippedValue; } std::string stripErrorEnumPrefix(std::string const& enumName) { assert(isErrorEnum(enumName)); return strip(enumName, "eError"); } std::string stripPluralS(std::string const& name) { std::string strippedName(name); size_t pos = strippedName.rfind('s'); assert(pos != std::string::npos); strippedName.erase(pos, 1); return strippedName; } std::vector tokenize(std::string tokenString, char separator) { std::vector tokens; size_t start = 0, end; do { end = tokenString.find(separator, start); tokens.push_back(tokenString.substr(start, end - start)); start = end + 1; } while (end != std::string::npos); 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 toCamelCase(std::string const& value) { assert(!value.empty() && (isupper(value[0]) || isdigit(value[0]))); std::string result; result.reserve(value.size()); result.push_back(value[0]); for (size_t i = 1; i < value.size(); i++) { if (value[i] != '_') { if ((value[i - 1] == '_') || isdigit(value[i - 1])) { result.push_back(value[i]); } else { result.push_back(static_cast(tolower(value[i]))); } } } return result; } std::string toUpperCase(std::string const& name) { std::string convertedName; for (size_t i = 0; i(toupper(name[i]))); } return convertedName; } void writeFunctionHeaderName(std::ostream & os, std::string const& name, bool singular, bool unique) { os << (singular ? stripPluralS(name) : name); if (unique) { os << "Unique"; } } void writeReinterpretCast(std::ostream & os, bool leadingConst, bool vulkanType, std::string const& type, bool trailingPointerToConst) { os << "reinterpret_cast<"; if (leadingConst) { os << "const "; } if (vulkanType) { os << "Vk"; } os << type; if (trailingPointerToConst) { os << "* const"; } os << "*>"; } void writeStandardOrEnhanced(std::ostream & os, std::string const& standard, std::string const& enhanced) { if (standard == enhanced) { // standard and enhanced string are equal -> just use one of them and we're done os << standard; } else { // standard and enhanced string differ -> use both, wrapping the enhanced by !VULKAN_HPP_DISABLE_ENHANCED_MODE // determine the argument list of that standard, and compare it with that of the enhanced // if they are equal -> need to have just one; if they differ -> need to have both size_t standardStart = standard.find('('); size_t standardCount = standard.find(')', standardStart) - standardStart; size_t enhancedStart = enhanced.find('('); bool unchangedInterface = (standard.substr(standardStart, standardCount) == enhanced.substr(enhancedStart, standardCount)); if (unchangedInterface) { os << "#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE" << std::endl; } os << standard << (unchangedInterface ? "#else" : "#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE") << std::endl << enhanced << "#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/" << std::endl; } } void writeTypesafeCheck(std::ostream & os, std::string const& typesafeCheck) { os << "// 32-bit vulkan is not typesafe for handles, so don't allow copy constructors on this platform by default." << std::endl << "// To enable this feature on 32-bit platforms please define VULKAN_HPP_TYPESAFE_CONVERSION" << std::endl << typesafeCheck << std::endl << "# if !defined( VULKAN_HPP_TYPESAFE_CONVERSION )" << std::endl << "# define VULKAN_HPP_TYPESAFE_CONVERSION" << std::endl << "# endif" << std::endl << "#endif" << std::endl; } void writeVersionCheck(std::ostream & os, std::string const& version) { os << "static_assert( VK_HEADER_VERSION == " << version << " , \"Wrong VK_HEADER_VERSION!\" );" << std::endl << std::endl; } #if !defined(NDEBUG) void skipFeatureRequire(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "name",{} } }); checkElements(getChildElements(element), {}); } void skipImplicitExternSyncParams(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); std::vector children = getChildElements(element); checkOrderedElements(children, { "param" }); checkEmptyElement(children[0]); } void skipTypeEnum(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category",{ "enum" } } }, { { "alias", {} }, { "name",{} } }); checkElements(getChildElements(element), {}); } void skipTypeInclude(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "category",{ "include" } } }, { { "name",{} } }); std::vector children = getChildElements(element); checkElements(children, { "name" }); for (auto child : children) { checkEmptyElement(child); } } #endif template void VulkanHppGenerator::checkAlias(std::map const& data, std::string const& name, int line) { if (data.find(name) == data.end()) { std::stringstream ss; ss << line; std::string lineNumber = ss.str(); throw std::runtime_error("Spec error on line " + lineNumber + ": missing alias <" + name + ">"); } } bool VulkanHppGenerator::containsUnion(std::string const& type, std::map const& structs) { // a simple recursive check if a type is or contains a union std::map::const_iterator sit = structs.find(type); bool found = (sit != structs.end()); if (found) { found = sit->second.isUnion; for (std::vector::const_iterator mit = sit->second.members.begin(); mit != sit->second.members.end() && !found; ++mit) { found = (mit->type == mit->pureType) && containsUnion(mit->type, structs); } } return found; } std::map VulkanHppGenerator::createDefaults() { std::map defaultValues; for (auto const& dependency : m_dependencies) { assert(defaultValues.find(dependency.name) == defaultValues.end()); switch (dependency.category) { case DependencyData::Category::BITMASK: case DependencyData::Category::HANDLE: case DependencyData::Category::STRUCT: case DependencyData::Category::UNION: // just call the default constructor for bitmasks, handles, structs, and unions (which are mapped to classes) defaultValues[dependency.name] = dependency.name + "()"; break; case DependencyData::Category::COMMAND: // commands should never be asked for defaults break; case DependencyData::Category::ENUM: assert(m_enums.find(dependency.name) != m_enums.end()); setDefault(dependency.name, defaultValues, m_enums.find(dependency.name)->second); break; case DependencyData::Category::FUNC_POINTER: // func_pointers default to nullptr defaultValues[dependency.name] = "nullptr"; break; case DependencyData::Category::REQUIRED: // all required default to "0" case DependencyData::Category::SCALAR: // all scalars default to "0" defaultValues[dependency.name] = "0"; break; default: assert(false && "Unhandled exception category"); break; } } return defaultValues; } void VulkanHppGenerator::determineEnhancedReturnType(CommandData & commandData) { std::string returnType; // if there is a return parameter of type void or Result, and if it's of type Result it either has just one success code // or two success codes, where the second one is of type eIncomplete and it's a two-step process // -> we can return that parameter if ((commandData.returnParam != ~0) && ((commandData.returnType == "void") || ((commandData.returnType == "Result") && ((commandData.successCodes.size() == 1) || ((commandData.successCodes.size() == 2) && (commandData.successCodes[1] == "eIncomplete") && commandData.twoStep))))) { if (commandData.vectorParams.find(commandData.returnParam) != commandData.vectorParams.end()) { // the return parameter is a vector-type parameter if (commandData.params[commandData.returnParam].pureType == "void") { // for a vector of void, we use a vector of uint8_t, instead commandData.enhancedReturnType = "std::vector"; } else { // for the other parameters, we use a vector of the pure type commandData.enhancedReturnType = "std::vector<" + commandData.params[commandData.returnParam].pureType + ",Allocator>"; } } else { // it's a simple parameter -> get the type and just remove the trailing '*' (originally, it's a pointer) assert(commandData.params[commandData.returnParam].type.back() == '*'); assert(commandData.params[commandData.returnParam].type.find("const") == std::string::npos); commandData.enhancedReturnType = commandData.params[commandData.returnParam].type; commandData.enhancedReturnType.pop_back(); } } else if ((commandData.returnType == "Result") && (commandData.successCodes.size() == 1)) { // an original return of type "Result" with just one successCode is changed to void, errors throw an exception commandData.enhancedReturnType = "void"; } else { // the return type just stays the original return type commandData.enhancedReturnType = commandData.returnType; } } void VulkanHppGenerator::determineReducedName(CommandData & commandData) { commandData.reducedName = commandData.fullName; std::string searchName = commandData.params[0].pureType; size_t pos = commandData.fullName.find(searchName); if ((pos == std::string::npos) && isupper(searchName[0])) { searchName[0] = static_cast(tolower(searchName[0])); pos = commandData.fullName.find(searchName); } if (pos != std::string::npos) { commandData.reducedName.erase(pos, searchName.length()); } else if ((searchName == "commandBuffer") && (commandData.fullName.find("cmd") == 0)) { commandData.reducedName.erase(0, 3); pos = 0; } if ((pos == 0) && isupper(commandData.reducedName[0])) { commandData.reducedName[0] = static_cast(tolower(commandData.reducedName[0])); } } void VulkanHppGenerator::determineReturnParam(CommandData & commandData) { // for return types of type Result or void, we can replace determine a parameter to return if ((commandData.returnType == "Result") || (commandData.returnType == "void")) { for (size_t i = 0; i < commandData.params.size(); i++) { if ((commandData.params[i].type.find('*') != std::string::npos) && (commandData.params[i].type.find("const") == std::string::npos) && std::find_if(commandData.vectorParams.begin(), commandData.vectorParams.end(), [i](std::pair const& vp) { return vp.second == i; }) == commandData.vectorParams.end() && ((commandData.vectorParams.find(i) == commandData.vectorParams.end()) || commandData.twoStep || (commandData.successCodes.size() == 1))) { // it's a non-const pointer, not a vector-size parameter, if it's a vector parameter, its a two-step process or there's just one success code // -> look for another non-cost pointer argument auto paramIt = std::find_if(commandData.params.begin() + i + 1, commandData.params.end(), [](ParamData const& pd) { return (pd.type.find('*') != std::string::npos) && (pd.type.find("const") == std::string::npos); }); // if there is another such argument, we can't decide which one to return -> return none (~0) // otherwise return the index of the selcted parameter commandData.returnParam = paramIt != commandData.params.end() ? ~0 : i; } } } } void VulkanHppGenerator::determineSkippedParams(CommandData & commandData) { // the size-parameters of vector parameters are not explicitly used in the enhanced API std::for_each(commandData.vectorParams.begin(), commandData.vectorParams.end(), [&commandData](std::pair const& vp) { // skip output-vector params which are indicated through a pointer. if (vp.second != ~0 // it is an vector // and it's not a get function with a fixed size input && !(commandData.fullName.find("get") == 0 && commandData.params[vp.second].unchangedType.find("*") == std::string::npos && vp.first == commandData.returnParam)) { commandData.skippedParams.insert(vp.second); } }); // and the return parameter is also skipped if (commandData.returnParam != ~0) { commandData.skippedParams.insert(commandData.returnParam); } } void VulkanHppGenerator::determineTemplateParam(CommandData & commandData) { for (size_t i = 0; i < commandData.params.size(); i++) { // any vector parameter on the pure type void is templatized in the enhanced API if ((commandData.vectorParams.find(i) != commandData.vectorParams.end()) && (commandData.params[i].pureType == "void")) { #if !defined(NDEBUG) for (size_t j = i + 1; j < commandData.params.size(); j++) { assert((commandData.vectorParams.find(j) == commandData.vectorParams.end()) || (commandData.params[j].pureType != "void")); } #endif commandData.templateParam = i; break; } } assert((commandData.templateParam == ~0) || (commandData.vectorParams.find(commandData.templateParam) != commandData.vectorParams.end())); } void VulkanHppGenerator::determineVectorParams(CommandData & commandData) { // look for the parameters whose len equals the name of an other parameter for (auto it = commandData.params.begin(), begin = it, end = commandData.params.end(); it != end; ++it) { if (!it->len.empty()) { auto findLambda = [it](ParamData const& pd) { return pd.name == it->len; }; auto findIt = std::find_if(begin, it, findLambda); // look for a parameter named as the len of this parameter assert((std::count_if(begin, end, findLambda) == 0) || (findIt < it)); // make sure, there is no other parameter like that // add this parameter as a vector parameter, using the len-name parameter as the second value (or ~0 if there is nothing like that) commandData.vectorParams.insert(std::make_pair(std::distance(begin, it), findIt < it ? std::distance(begin, findIt) : ~0)); assert((commandData.vectorParams[std::distance(begin, it)] != ~0) || (it->len == "null-terminated") || (it->len == "pAllocateInfo::descriptorSetCount") || (it->len == "pAllocateInfo::commandBufferCount")); } } } std::string VulkanHppGenerator::generateCall(CommandData const& commandData, bool firstCall, bool singular) { std::ostringstream call; writeCall(call, commandData, firstCall, singular); return call.str(); } std::string const& VulkanHppGenerator::getTypesafeCheck() const { return m_typesafeCheck; } std::string const& VulkanHppGenerator::getVersion() const { return m_version; } std::string const& VulkanHppGenerator::getVulkanLicenseHeader() const { return m_vulkanLicenseHeader; } bool VulkanHppGenerator::isSubStruct(std::pair const& nsd, std::string const& name, StructData const& structData) { if ((nsd.first != name) && (nsd.second.members.size() < structData.members.size()) && (structData.members[0].name != "sType")) { bool equal = true; for (size_t i = 0; i < nsd.second.members.size() && equal; i++) { equal = (nsd.second.members[i].type == structData.members[i].type) && (nsd.second.members[i].name == structData.members[i].name); } if (equal) { return true; } } return false; } void VulkanHppGenerator::linkCommandToHandle(CommandData & commandData) { // first, find the handle named like the type of the first argument // if there is no such handle, look for the unnamed "handle", that gathers all the functions not tied to a specific handle assert(!commandData.params.empty()); std::map::iterator hit = m_handles.find(commandData.params[0].pureType); if (hit == m_handles.end()) { hit = m_handles.find(""); } assert(hit != m_handles.end()); // put the command into the handle's list of commands, and store the handle in the commands className hit->second.commands.push_back(commandData.fullName); commandData.className = hit->first; // add the dependencies of the command to the dependencies of the handle DependencyData const& commandDD = m_dependencies.back(); std::list::iterator handleDD = std::find_if(m_dependencies.begin(), m_dependencies.end(), [hit](DependencyData const& dd) { return dd.name == hit->first; }); assert((handleDD != m_dependencies.end()) || hit->first.empty()); if (handleDD != m_dependencies.end()) { std::copy_if(commandDD.dependencies.begin(), commandDD.dependencies.end(), std::inserter(handleDD->dependencies, handleDD->dependencies.end()), [hit](std::string const& d) { return d != hit->first; }); } } bool VulkanHppGenerator::readCommandParam(tinyxml2::XMLElement const* element, std::set & dependencies, std::vector & params) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "externsync",{} },{ "len",{} },{ "noautovalidity",{ "true" } },{ "optional",{ "false", "true" } } }); checkElements(getChildElements(element), { "name", "type" }); ParamData param; bool isTwoStep = false; auto lenAttribute = attributes.find("len"); if (lenAttribute != attributes.end()) { param.len = lenAttribute->second; auto pit = std::find_if(params.begin(), params.end(), [¶m](ParamData const& pd) { return param.len == pd.name; }); if (pit != params.end()) { isTwoStep = (pit->type.find('*') != std::string::npos); } } // get the type of the parameter, and put it into the list of dependencies tinyxml2::XMLNode const* child = readCommandParamType(element->FirstChild(), param); dependencies.insert(param.pureType); assert(child->ToElement()); tinyxml2::XMLElement const* nameElement = child->ToElement(); checkEmptyElement(nameElement); param.name = child->ToElement()->GetText(); param.arraySize = readArraySize(child, param.name); auto optionalAttribute = attributes.find("optional"); param.optional = (optionalAttribute != attributes.end()) && (optionalAttribute->second == "true"); params.push_back(param); assert(!isTwoStep || (param.type.substr(0, 6) != "const ")); return isTwoStep; } tinyxml2::XMLNode const* VulkanHppGenerator::readCommandParamType(tinyxml2::XMLNode const* node, ParamData& param) { assert(node); if (node->ToText()) { // start type with "const" or "struct", if needed std::string value = trim(node->Value()); assert((value == "const") || (value == "struct") || (value == "const struct")); param.type = value + " "; node = node->NextSibling(); assert(node); } // get the pure type assert(node->ToElement()); tinyxml2::XMLElement const* typeElement = node->ToElement(); checkEmptyElement(typeElement); std::string type = strip(node->ToElement()->GetText(), "Vk"); param.unchangedType = param.type + node->ToElement()->GetText(); param.type += type; param.pureType = type; // end with "*", "**", or "* const*", if needed node = node->NextSibling(); assert(node); if (node->ToText()) { std::string value = trimEnd(node->Value()); assert((value == "*") || (value == "**") || (value == "* const*")); param.type += value; param.unchangedType += value; node = node->NextSibling(); } return node; } void VulkanHppGenerator::readCommands(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } }); std::vector children = getChildElements(element); checkElements(children, { "command" }); for (auto child : children) { readCommandsCommand(child); } } void VulkanHppGenerator::readCommandsCommand(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "alias", {} }, { "cmdbufferlevel",{ "primary", "secondary" } }, { "comment",{} }, { "errorcodes",{} }, { "name", {} }, { "pipeline",{ "compute", "graphics", "transfer" } }, { "queues",{ "compute", "graphics", "sparse_binding", "transfer" } }, { "renderpass",{ "both", "inside", "outside" } }, { "successcodes",{} } }); std::vector children = getChildElements(element); CommandData commandData; auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { // for command aliases, create a copy of the aliased command checkAttributes(attributes, element->GetLineNum(), { { "alias",{} },{ "name",{} } }, {}); // re-check on alias type! checkElements(children, {}); std::string alias = startLowerCase(strip(aliasIt->second, "vk")); checkAlias(m_commands, alias, element->GetLineNum()); auto commandsIt = m_commands.find(alias); assert(commandsIt != m_commands.end()); commandData = commandsIt->second; commandData.fullName = startLowerCase(strip(attributes.find("name")->second, "vk")); commandData.isAlias = true; determineReducedName(commandData); linkCommandToHandle(commandData); // add a DependencyData to this name m_dependencies.push_back(DependencyData(DependencyData::Category::COMMAND, commandData.fullName)); m_dependencies.back().dependencies.insert(alias); } else { checkElements(children, { "implicitexternsyncparams", "param", "proto" }); // read the success codes auto successcodesAttribute = attributes.find("successcodes"); if (successcodesAttribute != attributes.end()) { commandData.successCodes = tokenize(successcodesAttribute->second, ','); for (auto & code : commandData.successCodes) { std::string tag = findTag(code, m_tags); // on each success code: prepend 'e', strip "VK_" and a tag, convert it to camel case, and add the tag again code = std::string("e") + toCamelCase(strip(code, "VK_", tag)) + tag; } } for (auto child : children) { std::string value = child->Value(); if (value == "param") { commandData.twoStep |= readCommandParam(child, m_dependencies.back().dependencies, commandData.params); } else if (value == "proto") { readCommandProto(child, commandData.returnType, commandData.unchangedReturnType, commandData.fullName); } #if !defined(NDEBUG) else { assert(value == "implicitexternsyncparams"); skipImplicitExternSyncParams(child); } #endif } determineReducedName(commandData); linkCommandToHandle(commandData); registerDeleter(commandData); determineVectorParams(commandData); determineReturnParam(commandData); determineTemplateParam(commandData); determineEnhancedReturnType(commandData); determineSkippedParams(commandData); } // insert the commandData into the commands-map, assert(m_commands.find(commandData.fullName) == m_commands.end()); m_commands.insert(std::make_pair(commandData.fullName, commandData)); } void VulkanHppGenerator::readCommandProto(tinyxml2::XMLElement const* element, std::string & returnType, std::string & unchangedReturnType, std::string & fullName) { checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); std::vector children = getChildElements(element); checkOrderedElements(children, { "type", "name" }); // get return type and name of the command returnType = strip(children[0]->GetText(), "Vk"); unchangedReturnType = children[0]->GetText(); fullName = startLowerCase(strip(children[1]->GetText(), "vk")); // add an empty DependencyData to this name m_dependencies.push_back(DependencyData(DependencyData::Category::COMMAND, fullName)); } void VulkanHppGenerator::readComment(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); checkElements(getChildElements(element), {}); assert(element->GetText()); std::string text = element->GetText(); if (text.find("\nCopyright") == 0) { assert(m_vulkanLicenseHeader.empty()); m_vulkanLicenseHeader = text; // erase the part after the Copyright text size_t pos = m_vulkanLicenseHeader.find("\n\n------------------------------------------------------------------------"); if (pos != std::string::npos) { m_vulkanLicenseHeader.erase(pos); } // replace any '\n' with "\n// " for (pos = m_vulkanLicenseHeader.find('\n'); pos != std::string::npos; pos = m_vulkanLicenseHeader.find('\n', pos + 1)) { m_vulkanLicenseHeader.replace(pos, 1, "\n// "); } // 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.erase(m_vulkanLicenseHeader.begin(), std::find_if(m_vulkanLicenseHeader.begin(), m_vulkanLicenseHeader.end(), [](char c) { return !std::isspace(c); })); } void VulkanHppGenerator::readDisabledExtensionRequire(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); std::vector children = getChildElements(element); checkElements(children, { "command", "enum", "type" }); for (auto child : children) { checkElements(getChildElements(child), {}); std::string value = child->Value(); if ((value == "command") || (value == "type")) { std::map attributes = getAttributes(child); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); // disable a command or a type ! auto nameAttribute = attributes.find("name"); std::string name = (value == "command") ? startLowerCase(strip(nameAttribute->second, "vk")) : strip(nameAttribute->second, "Vk"); // search this name in the dependencies list and remove it std::list::const_iterator depIt = std::find_if(m_dependencies.begin(), m_dependencies.end(), [&name](DependencyData const& dd) { return(dd.name == name); }); assert(depIt != m_dependencies.end()); m_dependencies.erase(depIt); // erase it from all dependency sets for (auto & dep : m_dependencies) { dep.dependencies.erase(name); } if (value == "command") { // first unlink the command from its class auto commandsIt = m_commands.find(name); assert(commandsIt != m_commands.end()); assert(!commandsIt->second.className.empty()); auto handlesIt = m_handles.find(commandsIt->second.className); assert(handlesIt != m_handles.end()); auto it = std::find(handlesIt->second.commands.begin(), handlesIt->second.commands.end(), name); assert(it != handlesIt->second.commands.end()); handlesIt->second.commands.erase(it); // then remove the command m_commands.erase(name); } else { // a type simply needs to be removed from the structs and vkTypes sets assert((m_structs.find(name) != m_structs.end()) && (m_vkTypes.find(name) != m_vkTypes.end())); m_structs.erase(name); m_vkTypes.erase(name); } } else { assert(value == "enum"); std::map attributes = getAttributes(child); checkAttributes(attributes, child->GetLineNum(), { { "name",{} } }, { { "bitpos", {} }, { "extends",{} },{ "offset",{} },{ "value",{} } }); } } } void VulkanHppGenerator::readEnums(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "comment",{} },{ "type",{ "bitmask", "enum" } } }); std::vector children = getChildElements(element); checkElements(children, { "comment", "enum", "unused" }); std::string name = strip(attributes.find("name")->second, "Vk"); if (name == "API Constants") { for (auto child : children) { assert(strcmp(child->Value(), "enum") == 0); readEnumsConstant(child); } } else { checkAttributes(attributes, element->GetLineNum(), { { "name",{} },{ "type",{ "bitmask", "enum" } } }, { { "comment",{} } }); // re-check with type as required if (std::find_if(m_dependencies.begin(), m_dependencies.end(), [&name](DependencyData const& dd) { return dd.name == name; }) == m_dependencies.end()) { // add an empty DependencyData on this name into the dependencies list m_dependencies.push_back(DependencyData(DependencyData::Category::ENUM, name)); // add this enum to the set of Vulkan data types assert(m_vkTypes.find(name) == m_vkTypes.end()); m_vkTypes.insert(name); } // ad an empty EnumData on this name into the enums map std::map::iterator it = m_enums.insert(std::make_pair(name, EnumData(name))).first; assert(it->second.postfix.empty() && it->second.prefix.empty() && it->second.protect.empty() && it->second.values.empty()); if (name == "Result") { // special handling for VKResult, as its enums just have VK_ in common it->second.prefix = "VK_"; } else { std::string type = attributes.find("type")->second; it->second.bitmask = (type == "bitmask"); if (it->second.bitmask) { // for a bitmask enum, start with "VK", cut off the trailing "FlagBits", and convert that name to upper case // end that with "Bit" size_t pos = name.find("FlagBits"); assert(pos != std::string::npos); it->second.prefix = "VK" + toUpperCase(name.substr(0, pos)) + "_"; } else { // for a non-bitmask enum, start with "VK", and convert the name to upper case it->second.prefix = "VK" + toUpperCase(name) + "_"; } // if the enum name contains a tag move it from the prefix to the postfix to generate correct enum value names. for (std::set::const_iterator tit = m_tags.begin(); tit != m_tags.end(); ++tit) { if ((tit->length() < it->second.prefix.length()) && (it->second.prefix.substr(it->second.prefix.length() - tit->length() - 1) == (*tit + "_"))) { it->second.prefix.erase(it->second.prefix.length() - tit->length() - 1); it->second.postfix = "_" + *tit; break; } else if ((tit->length() < it->second.name.length()) && (it->second.name.substr(it->second.name.length() - tit->length()) == *tit)) { it->second.postfix = "_" + *tit; break; } } } // read the names of the enum values for (auto child : children) { std::string value = child->Value(); if (value == "enum") { readEnumsEnum(child, it->second, ""); } #if !defined(NDEBUG) else { assert((value == "comment") || (value == "unused")); } #endif } } } void VulkanHppGenerator::readEnumsEnum(tinyxml2::XMLElement const* element, EnumData & enumData, std::string const& tag) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { {"alias", {} }, { "bitpos",{} },{ "comment",{} },{ "value",{} } }); assert((attributes.find("alias") != attributes.end()) + (attributes.find("bitpos") != attributes.end()) + (attributes.find("value") != attributes.end()) == 1); checkElements(getChildElements(element), {}); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { auto enumIt = std::find_if(enumData.values.begin(), enumData.values.end(), [&aliasIt](EnumValueData const& evd) { return evd.value == aliasIt->second; }); assert((enumIt != enumData.values.end()) && enumIt->alias.empty()); enumIt->alias = createEnumValueName(attributes.find("name")->second, enumData.prefix, enumData.postfix, enumData.bitmask, tag); } else { enumData.addEnumValue(attributes.find("name")->second, tag, m_nameMap); } } void VulkanHppGenerator::readEnumsConstant(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "alias", {}}, { "comment",{} }, { "value",{} } }); checkElements(getChildElements(element), {}); std::string name = attributes.find("name")->second; assert(m_constants.find(name) == m_constants.end()); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { checkAttributes(attributes, element->GetLineNum(), { {"alias", {}}, { "name", {}} }, {}); // re-check on alias type checkAlias(m_constants, aliasIt->second, element->GetLineNum()); m_constants[name] = m_constants.find(aliasIt->second)->second; } else { checkAttributes(attributes, element->GetLineNum(), { { "name",{} }, { "value", {}} }, { {"comment", {} } }); // re-check on non-alias type m_constants[name] = attributes.find("value")->second; } } void VulkanHppGenerator::readExtensionCommand(tinyxml2::XMLElement const* element, std::string const& protect) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); checkElements(getChildElements(element), {}); // just add the protect string to the CommandData if (!protect.empty()) { std::string name = startLowerCase(strip(attributes.find("name")->second, "vk")); std::map::iterator cit = m_commands.find(name); assert(cit != m_commands.end()); cit->second.protect = protect; } } void VulkanHppGenerator::readExtensionEnum(tinyxml2::XMLElement const* element, std::string const& tag) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name", {} } }, { { "alias", {} }, { "bitpos", {} }, { "comment", {} }, { "dir", { "-" } }, { "extends", {} }, { "extnumber", {} }, { "offset", {} }, { "value", {} } }); checkElements(getChildElements(element), {}); // TODO process enums which don't extend existing enums auto extendsIt = attributes.find("extends"); if (extendsIt != attributes.end()) { std::string extends = strip(extendsIt->second, "Vk"); auto enumIt = m_enums.find(extends); assert(enumIt != m_enums.end()); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { checkAttributes(attributes, element->GetLineNum(), { { "alias", {} }, { "extends", {} }, { "name", {} } }, { { "comment",{} } }); std::string alias = createEnumValueName(aliasIt->second, enumIt->second.prefix, enumIt->second.postfix, enumIt->second.bitmask, tag); auto evdIt = std::find_if(enumIt->second.values.begin(), enumIt->second.values.end(), [&alias](EnumValueData const& evd) { return evd.name == alias; }); assert(evdIt != enumIt->second.values.end()); evdIt->alias = createEnumValueName(attributes.find("name")->second, enumIt->second.prefix, enumIt->second.postfix, enumIt->second.bitmask, tag); if (evdIt->name == evdIt->alias) { // skip alias, that would result in the very same enum name evdIt->alias.clear(); } } else { assert((attributes.find("bitpos") != attributes.end()) + (attributes.find("offset") != attributes.end()) + (attributes.find("value") != attributes.end()) == 1); enumIt->second.addEnumValue(attributes.find("name")->second, tag, m_nameMap); } } } void VulkanHppGenerator::readExtensionRequire(tinyxml2::XMLElement const* element, std::string const& protect, std::string const& tag) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "extension",{} },{ "feature",{} } }); std::vector children = getChildElements(element); checkElements(children, { "command", "comment", "enum", "type" }); for (auto child : children) { std::string value = child->Value(); if (value == "command") { readExtensionCommand(child, protect); } else if (value == "enum") { readExtensionEnum(child, tag); } else if (value == "type") { readExtensionType(child, protect); } #if !defined(NDEBUG) else { assert(value == "comment"); checkEmptyElement(child); } #endif } } void VulkanHppGenerator::readExtensions(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); checkElements(children, { "extension" }); for (auto child : children) { readExtensionsExtension(child); } } void VulkanHppGenerator::readExtensionsExtension(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} }, { "number",{} }, { "supported",{ "disabled", "vulkan" } } }, { { "author",{} }, { "comment", {} }, { "contact",{} }, { "deprecatedby", {} }, { "obsoletedby", {} }, { "platform",{} }, { "promotedto", {} }, { "provisional", {} }, { "protect",{} }, { "requires",{} }, { "requiresCore",{} }, { "type",{ "device", "instance" } } }); std::vector children = getChildElements(element); checkElements(children, { "require" }); if (attributes.find("supported")->second == "disabled") { // kick out all the disabled stuff we've read before !! for (tinyxml2::XMLElement const* child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { assert(strcmp(child->Value(), "require") == 0); readDisabledExtensionRequire(child); } } else { std::string name = attributes.find("name")->second; std::string tag = extractTag(name); assert(m_tags.find(tag) != m_tags.end()); auto protectAttribute = attributes.find("protect"); auto platformAttribute = attributes.find("platform"); std::string protect; if (protectAttribute != attributes.end()) { protect = protectAttribute->second; } else if (platformAttribute != attributes.end()) { auto authorAttribute = attributes.find("author"); assert(authorAttribute != attributes.end()); protect = "VK_USE_PLATFORM_" + toUpperCase(platformAttribute->second) + "_" + authorAttribute->second; } #if !defined(NDEBUG) assert(m_extensions.find(name) == m_extensions.end()); ExtensionData & extension = m_extensions.insert(std::make_pair(name, ExtensionData())).first->second; extension.protect = protect; auto requiresAttribute = attributes.find("requires"); if (requiresAttribute != attributes.end()) { extension.requires = tokenize(requiresAttribute->second, ','); } #endif for (auto child : children) { readExtensionRequire(child, protect, tag); } } } void VulkanHppGenerator::readExtensionType(tinyxml2::XMLElement const* element, std::string const& protect) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); checkElements(getChildElements(element), {}); // add the protect-string to the appropriate type: enum, flag, handle, scalar, or struct if (!protect.empty()) { std::string name = strip(attributes.find("name")->second, "Vk"); std::map::iterator bitmasksIt = m_bitmasks.find(name); if (bitmasksIt != m_bitmasks.end()) { bitmasksIt->second.protect = protect; // if the enum of this flags is auto-generated, protect it as well std::string enumName = generateEnumNameForFlags(name); std::map::iterator enumsIt = m_enums.find(enumName); assert(enumsIt != m_enums.end()); if (enumsIt->second.values.empty()) { enumsIt->second.protect = protect; } } else { std::map::iterator eit = m_enums.find(name); if (eit != m_enums.end()) { eit->second.protect = protect; } else { std::map::iterator hait = m_handles.find(name); if (hait != m_handles.end()) { hait->second.protect = protect; } else { std::map::iterator scit = m_scalars.find(name); if (scit != m_scalars.end()) { scit->second.protect = protect; } else { std::map::iterator stit = m_structs.find(name); if (stit != m_structs.end()) { stit->second.protect = protect; } else { assert(m_defines.find(name) != m_defines.end()); } } } } } } } void VulkanHppGenerator::readFeature(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "api",{ "vulkan" } },{ "comment",{} },{ "name",{} },{ "number",{} } }, {}); std::vector children = getChildElements(element); checkElements(children, { "require" }); for (auto child : children) { readFeatureRequire(child); } } void VulkanHppGenerator::readFeatureRequire(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } }); std::vector children = getChildElements(element); checkElements(children, { "command", "comment", "enum", "type" }); for (auto child : children) { std::string value = child->Value(); if (value == "enum") { readFeatureRequireEnum(child); } #if !defined(NDEBUG) else { assert((value == "command") || (value == "comment") || (value == "type")); skipFeatureRequire(child); } #endif } } void VulkanHppGenerator::readFeatureRequireEnum(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "bitpos",{} }, { "comment",{} }, { "dir", { "-" } }, { "extends",{} }, { "extnumber", {} }, { "offset", {} }, { "value",{} } }); checkElements(getChildElements(element), {}); auto extendsAttribute = attributes.find("extends"); if (extendsAttribute != attributes.end()) { assert(strncmp(extendsAttribute->second.c_str(), "Vk", 2) == 0); std::string extends = strip(extendsAttribute->second, "Vk"); auto enumIt = m_enums.find(extends); assert(enumIt != m_enums.end()); enumIt->second.addEnumValue(attributes.find("name")->second, "", m_nameMap); } } void VulkanHppGenerator::readTags(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); checkElements(children, { "tag" }); for (auto child : children) { std::string value = child->Value(); assert(value == "tag"); readTag(child); } } void VulkanHppGenerator::readTag(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "author",{} },{ "contact",{} },{ "name",{} } }, {}); checkElements(getChildElements(element), {}); for (auto const& attribute : attributes) { std::string name = attribute.first; if (name == "name") { std::string value = attribute.second; m_tags.insert(value); } else { assert((name == "author") || (name == "contact")); } } } void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); auto categoryIt = attributes.find("category"); if (categoryIt != attributes.end()) { if (categoryIt->second == "basetype") { readTypeBasetype(element, attributes); } else if (categoryIt->second == "bitmask") { readTypeBitmask(element, attributes); } else if (categoryIt->second == "define") { readTypeDefine(element, attributes); } else if (categoryIt->second == "funcpointer") { readTypeFuncpointer(element, attributes); } else if (categoryIt->second == "handle") { readTypeHandle(element, attributes); } else if (categoryIt->second == "struct") { readTypeStruct(element, false, attributes); } else if (categoryIt->second == "union") { readTypeStruct(element, true, attributes); } #if !defined(NDEBUG) else if (categoryIt->second == "enum") { skipTypeEnum(element, attributes); } else if (categoryIt->second == "include") { skipTypeInclude(element); } else #else else if ((categoryIt->second != "enum") && (categoryIt->second != "include")) #endif { std::stringstream ss; ss << element->GetLineNum(); std::string lineNumber = ss.str(); throw std::runtime_error("Spec error on line " + lineNumber + ": unknown category <" + categoryIt->second + ">"); } } else { assert(attributes.find("name") != attributes.end()); readTypeName(element, attributes); } } void VulkanHppGenerator::readTypeBasetype(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category",{ "basetype" } } }, {}); std::vector children = getChildElements(element); checkOrderedElements(children, { "type", "name" }); checkEmptyElement(children[0]); checkEmptyElement(children[1]); std::string type = children[0]->GetText(); assert((type == "uint32_t") || (type == "uint64_t")); std::string name = strip(children[1]->GetText(), "Vk"); // skip "Flags", if (name != "Flags") { m_dependencies.push_back(DependencyData(DependencyData::Category::SCALAR, name)); m_dependencies.back().dependencies.insert(type); } else { assert(type == "uint32_t"); } } void VulkanHppGenerator::readTypeBitmask(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category", { "bitmask" } } }, { { "alias", {} }, { "name", {}}, { "requires", {} } }); std::vector children = getChildElements(element); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { checkAttributes(attributes, element->GetLineNum(), { { "alias", {} }, { "category", {"bitmask"} }, { "name", {} } }, {}); // re-check on alias type! checkElements(children, {}); std::string alias = strip(aliasIt->second, "Vk"); checkAlias(m_bitmasks, alias, element->GetLineNum()); std::string name = strip(attributes.find("name")->second, "Vk"); auto bitmasksIt = m_bitmasks.find(alias); assert((bitmasksIt != m_bitmasks.end()) && bitmasksIt->second.alias.empty()); bitmasksIt->second.alias = name; } else { checkOrderedElements(children, { "type", "name" }); checkEmptyElement(children[0]); checkEmptyElement(children[1]); assert(strcmp(children[0]->GetText(), "VkFlags") == 0); std::string name = strip(children[1]->GetText(), "Vk"); std::string requires; auto requiresIt = attributes.find("requires"); if (requiresIt != attributes.end()) { requires = strip(requiresIt->second, "Vk"); } else { // Generate FlagBits name, add a DependencyData for that name, and add it to the list of enums and vulkan types requires = generateEnumNameForFlags(name); assert(std::find_if(m_dependencies.begin(), m_dependencies.end(), [&requires](DependencyData const& dd) { return dd.name == requires; }) == m_dependencies.end()); m_dependencies.push_back(DependencyData(DependencyData::Category::ENUM, requires)); assert(m_enums.find(requires) == m_enums.end()); m_enums.insert(std::make_pair(requires, EnumData(requires, true))); assert(m_vkTypes.find(requires) == m_vkTypes.end()); m_vkTypes.insert(requires); } // add a DependencyData for the bitmask name, with the required type as its first dependency m_dependencies.push_back(DependencyData(DependencyData::Category::BITMASK, name)); m_dependencies.back().dependencies.insert(requires); m_bitmasks.insert(std::make_pair(name, BitmaskData())); assert(m_vkTypes.find(name) == m_vkTypes.end()); m_vkTypes.insert(name); } } void VulkanHppGenerator::readTypeDefine(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category",{ "define" } } }, { { "name",{} } }); auto nameIt = attributes.find("name"); if (nameIt != attributes.end()) { assert(!element->FirstChildElement()); assert(nameIt->second == "VK_DEFINE_NON_DISPATCHABLE_HANDLE"); // filter out the check for the different types of VK_DEFINE_NON_DISPATCHABLE_HANDLE std::string text = element->LastChild()->ToText()->Value(); size_t start = text.find("#if defined(__LP64__)"); size_t end = text.find_first_of("\r\n", start + 1); m_typesafeCheck = text.substr(start, end - start); } else if (element->GetText() && (trim(element->GetText()) == "struct")) { tinyxml2::XMLElement const* child = element->FirstChildElement(); assert(child && (strcmp(child->Value(), "name") == 0) && child->GetText()); m_defines.insert(child->GetText()); m_dependencies.push_back(DependencyData(DependencyData::Category::REQUIRED, child->GetText())); } else { tinyxml2::XMLElement const* child = element->FirstChildElement(); assert(child && !child->FirstAttribute() && (strcmp(child->Value(), "name") == 0) && child->GetText()); std::string text = trim(child->GetText()); if (text == "VK_HEADER_VERSION") { m_version = trimEnd(element->LastChild()->ToText()->Value()); } // ignore all the other defines assert(!child->NextSiblingElement() || (child->NextSiblingElement() && !child->NextSiblingElement()->FirstAttribute() && (strcmp(child->NextSiblingElement()->Value(), "type") == 0) && !child->NextSiblingElement()->NextSiblingElement())); } } void VulkanHppGenerator::readTypeFuncpointer(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category",{ "funcpointer" } } }, { { "requires",{} } }); std::vector children = getChildElements(element); checkElements(children, { "name", "type" }); assert(!children.empty()); checkEmptyElement(children[0]); assert((strcmp(children[0]->Value(), "name") == 0) && children[0]->GetText()); m_dependencies.push_back(DependencyData(DependencyData::Category::FUNC_POINTER, children[0]->GetText())); #if !defined(NDEBUG) for (size_t i = 1; i < children.size(); i++) { checkEmptyElement(children[i]); } #endif } void VulkanHppGenerator::readTypeHandle(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category",{ "handle" } } }, { { "alias",{} }, { "name",{} }, { "parent",{} } }); std::vector children = getChildElements(element); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { checkAttributes(attributes, element->GetLineNum(), { { "alias",{} },{ "category",{ "handle" } },{ "name",{} } }, {}); // re-check on alias type! checkElements(children, {}); std::string alias = strip(aliasIt->second, "Vk"); checkAlias(m_handles, alias, element->GetLineNum()); std::string name = strip(attributes.find("name")->second, "Vk"); auto handlesIt = m_handles.find(alias); assert((handlesIt != m_handles.end()) && handlesIt->second.alias.empty()); handlesIt->second.alias = name; } else { checkOrderedElements(children, { "type", "name" }); checkEmptyElement(children[0]); checkEmptyElement(children[1]); #if !defined(NDEBUG) std::string type = children[0]->GetText(); assert((type.find("VK_DEFINE_HANDLE") == 0) || (type.find("VK_DEFINE_NON_DISPATCHABLE_HANDLE") == 0)); #endif std::string name = strip(children[1]->GetText(), "Vk"); m_dependencies.push_back(DependencyData(DependencyData::Category::HANDLE, name)); assert(m_vkTypes.find(name) == m_vkTypes.end()); m_vkTypes.insert(name); assert(m_handles.find(name) == m_handles.end()); m_handles.insert(std::make_pair(name, HandleData())); } } void VulkanHppGenerator::readTypeName(tinyxml2::XMLElement const* element, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "requires",{} } }); checkElements(getChildElements(element), {}); auto nameIt = attributes.find("name"); assert(nameIt != attributes.end()); m_dependencies.push_back(DependencyData(DependencyData::Category::REQUIRED, nameIt->second)); } void VulkanHppGenerator::readTypes(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); checkElements(children, { "comment", "type" }); for (auto child : children) { std::string value = child->Value(); if (value == "type") { readType(child); } #if !defined(NDEBUG) else { assert(value == "comment"); checkEmptyElement(child); } #endif } } void VulkanHppGenerator::readTypeStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map const& attributes) { checkAttributes(attributes, element->GetLineNum(), { { "category",{ isUnion ? "union" : "struct" } }, { "name",{} } }, { { "alias", {} }, { "comment",{} }, { "returnedonly",{ "true" } }, { "structextends",{} } }); std::vector children = getChildElements(element); checkElements(children, { "comment", "member" }); std::string name = strip(attributes.find("name")->second, "Vk"); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { checkAttributes(attributes, element->GetLineNum(), { { "alias", {}}, {"category", {"struct"}}, { "name", {}} }, {}); // re-check on alias type! std::string alias = strip(aliasIt->second, "Vk"); checkAlias(m_structs, alias, element->GetLineNum()); auto structsIt = m_structs.find(alias); assert((structsIt != m_structs.end()) && structsIt->second.alias.empty()); structsIt->second.alias = name; } else { m_dependencies.push_back(DependencyData(isUnion ? DependencyData::Category::UNION : DependencyData::Category::STRUCT, name)); assert(m_structs.find(name) == m_structs.end()); std::map::iterator it = m_structs.insert(std::make_pair(name, StructData())).first; it->second.returnedOnly = (attributes.find("returnedonly") != attributes.end()); it->second.isUnion = isUnion; auto attributesIt = attributes.find("structextends"); if (attributesIt != attributes.end()) { std::vector structExtends = tokenize(attributesIt->second, ','); for (auto const& s : structExtends) { assert(s.substr(0, 2) == "Vk"); std::string strippedName = s.substr(2); it->second.structExtends.push_back(strippedName); m_extendedStructs.insert(strippedName); } assert(!it->second.structExtends.empty()); } for (auto child : children) { assert(child->Value()); std::string value = child->Value(); if (value == "member") { readTypeStructMember(child, it->second); } #if !defined(NDEBUG) else { assert(value == "comment"); checkEmptyElement(child); } #endif } for (auto const& s : m_structs) { if (isSubStruct(s, name, it->second)) { it->second.subStruct = s.first; break; // just take the very first candidate as a subStruct, skip any possible others! } } } assert(m_vkTypes.find(name) == m_vkTypes.end()); m_vkTypes.insert(name); } void VulkanHppGenerator::readTypeStructMember(tinyxml2::XMLElement const* element, StructData & structData) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "altlen",{} }, { "externsync",{ "true" } }, { "len",{} }, { "noautovalidity",{ "true" } }, { "optional",{ "false", "true" } }, { "values",{} } }); std::vector children = getChildElements(element); checkElements(children, { "comment", "enum", "name", "type" }); for (auto child : children) { checkEmptyElement(child); } structData.members.push_back(MemberData()); MemberData & member = structData.members.back(); auto valuesAttribute = attributes.find("values"); if (valuesAttribute != attributes.end()) { member.values = valuesAttribute->second; } tinyxml2::XMLNode const* child = element->FirstChild(); assert(child); if (child->ToText()) { std::string value = trim(child->Value()); assert((value == "const") || (value == "struct") || value == "const struct"); member.type = value + " "; child = child->NextSibling(); assert(child); } assert(child->ToElement()); tinyxml2::XMLElement const* typeElement = child->ToElement(); assert((strcmp(typeElement->Value(), "type") == 0) && typeElement->GetText()); member.pureType = strip(typeElement->GetText(), "Vk"); member.type += member.pureType; child = typeElement->NextSibling(); assert(child); if (child->ToText()) { std::string value = trimEnd(child->Value()); assert((value == "*") || (value == "**") || (value == "* const*")); member.type += value; child = child->NextSibling(); } m_dependencies.back().dependencies.insert(member.pureType); assert(child->ToElement()); tinyxml2::XMLElement const* nameElement = child->ToElement(); assert((strcmp(nameElement->Value(), "name") == 0) && nameElement->GetText()); member.name = nameElement->GetText(); member.arraySize = readArraySize(nameElement, member.name); } void VulkanHppGenerator::registerDeleter(CommandData const& commandData) { if ((commandData.fullName.substr(0, 7) == "destroy") || (commandData.fullName.substr(0, 4) == "free")) { std::string key; size_t valueIndex; switch (commandData.params.size()) { case 2: case 3: assert(commandData.params.back().pureType == "AllocationCallbacks"); key = (commandData.params.size() == 2) ? "" : commandData.params[0].pureType; valueIndex = commandData.params.size() - 2; break; case 4: key = commandData.params[0].pureType; valueIndex = 3; assert(m_deleters.find(commandData.params[valueIndex].pureType) == m_deleters.end()); m_deleters[commandData.params[valueIndex].pureType].pool = commandData.params[1].pureType; break; default: assert(false); valueIndex = 0; } assert(m_deleterTypes[key].find(commandData.params[valueIndex].pureType) == m_deleterTypes[key].end()); m_deleterTypes[key].insert(commandData.params[valueIndex].pureType); m_deleters[commandData.params[valueIndex].pureType].call = commandData.reducedName; } } void VulkanHppGenerator::setDefault(std::string const& name, std::map & defaultValues, EnumData const& enumData) { defaultValues[name] = name + (enumData.values.empty() ? "()" : ("::" + enumData.values.front().name)); } void VulkanHppGenerator::sortDependencies() { std::set listedTypes = { "VkFlags" }; std::list sortedDependencies; while (!m_dependencies.empty()) { bool found = false; for (std::list::iterator it = m_dependencies.begin(); it != m_dependencies.end(); ++it) { // check if all dependencies of it are already listed if (std::find_if(it->dependencies.begin(), it->dependencies.end(), [&listedTypes](std::string const& d) { return listedTypes.find(d) == listedTypes.end(); }) == it->dependencies.end()) { // add it to the end of the sorted list and the set of listed types, remove it from the list of dependencies to handle and start over with the next dependency sortedDependencies.push_back(*it); listedTypes.insert(it->name); // if it is a struct, add any alias of it to the list of encountered types if (it->category == DependencyData::Category::STRUCT) { std::map::const_iterator sit = m_structs.find(it->name); assert(sit != m_structs.end()); if (!sit->second.alias.empty()) { assert(listedTypes.find(sit->second.alias) == listedTypes.end()); listedTypes.insert(sit->second.alias); } } m_dependencies.erase(it); found = true; break; } } if (!found) { // at least one dependency of it is not yet listed -> resolve direct circular dependencies for (std::list::iterator it = m_dependencies.begin(); !found && it != m_dependencies.end(); ++it) { for (std::set::const_iterator dit = it->dependencies.begin(); dit != it->dependencies.end(); ++dit) { std::list::const_iterator depIt = std::find_if(m_dependencies.begin(), m_dependencies.end(), [&dit](DependencyData const& dd) { return(dd.name == *dit); }); if (depIt != m_dependencies.end()) { if (depIt->dependencies.find(it->name) != depIt->dependencies.end()) { // we only have two cases, for now! assert((it->category == DependencyData::Category::HANDLE) && (depIt->category == DependencyData::Category::STRUCT) || (it->category == DependencyData::Category::STRUCT) && (depIt->category == DependencyData::Category::STRUCT)); it->forwardDependencies.insert(*dit); it->dependencies.erase(*dit); found = true; break; } } #if !defined(NDEBUG) else { // here, only already sorted dependencies should occur, or structs that are aliased and sorted std::list::const_iterator sdit = std::find_if(sortedDependencies.begin(), sortedDependencies.end(), [&dit](DependencyData const& dd) { return(dd.name == *dit); }); if (sdit == sortedDependencies.end()) { std::map::const_iterator sit = std::find_if(m_structs.begin(), m_structs.end(), [&dit](std::pair const& sd) { return sd.second.alias == *dit; }); assert(sit != m_structs.end()); assert(std::find_if(sortedDependencies.begin(), sortedDependencies.end(), [name = sit->first](DependencyData const& dd) { return dd.name == name; }) != sortedDependencies.end()); } } #endif } } } assert(found); } m_dependencies.swap(sortedDependencies); } void VulkanHppGenerator::writeArguments(std::ostream & os, CommandData const& commandData, bool firstCall, bool singular, size_t from, size_t to) { assert(from <= to); // get the parameter indices of the counter for vector parameters std::map countIndices; for (std::map::const_iterator it = commandData.vectorParams.begin(); it != commandData.vectorParams.end(); ++it) { countIndices.insert(std::make_pair(it->second, it->first)); } bool encounteredArgument = false; for (size_t i = from; i < to; i++) { if (encounteredArgument) { os << ", "; } std::map::const_iterator it = countIndices.find(i); if (it != countIndices.end()) { writeCallCountParameter(os, commandData, singular, it); } else if ((it = commandData.vectorParams.find(i)) != commandData.vectorParams.end()) { writeCallVectorParameter(os, commandData, firstCall, singular, it); } else if (m_vkTypes.find(commandData.params[i].pureType) != m_vkTypes.end()) { writeCallVulkanTypeParameter(os, commandData.params[i]); } else { writeCallPlainTypeParameter(os, commandData.params[i]); } encounteredArgument = true; } } void VulkanHppGenerator::writeBitmaskToString(std::ostream & os, std::string const& bitmaskName, EnumData const &enumData) { // the helper functions to make strings out of flag values enterProtect(os, enumData.protect); os << " VULKAN_HPP_INLINE std::string to_string(" << bitmaskName << (enumData.values.empty() ? ")" : " value)") << std::endl << " {" << std::endl; if (enumData.values.empty()) { // no flags values in this enum -> return "{}" os << " return \"{}\";" << std::endl; } else { os << " if (!value) return \"{}\";" << std::endl << " std::string result;" << std::endl; // 'or' together all the bits in the value for (auto valuesIt = enumData.values.begin(); valuesIt != enumData.values.end(); ++valuesIt) { os << " if (value & " << enumData.name << "::" << valuesIt->name << ") result += \"" << valuesIt->name.substr(1) << " | \";" << std::endl; } // cut off the last three characters from the result (being " | ") os << " return \"{\" + result.substr(0, result.size() - 3) + \"}\";" << std::endl; } os << " }" << std::endl; leaveProtect(os, enumData.protect); os << std::endl; } void VulkanHppGenerator::writeCall(std::ostream & os, CommandData const& commandData, bool firstCall, bool singular) { // the original function call os << "d.vk" << startUpperCase(commandData.fullName) << "( "; if (!commandData.className.empty()) { // if it's member of a class -> the first argument is the member variable, starting with "m_" assert(commandData.className == commandData.params[0].type); os << "m_" << startLowerCase(commandData.className); if (1 < commandData.params.size()) { os << ", "; } } writeArguments(os, commandData, firstCall, singular, commandData.className.empty() ? 0 : 1, commandData.params.size()); os << " )"; } void VulkanHppGenerator::writeCallCountParameter(std::ostream & os, CommandData const& commandData, bool singular, std::map::const_iterator it) { // this parameter is a count parameter for a vector parameter if ((commandData.returnParam == it->second) && commandData.twoStep) { // the corresponding vector parameter is the return parameter and it's a two-step algorithm // -> use the pointer to a local variable named like the counter parameter without leading 'p' os << "&" << startLowerCase(strip(commandData.params[it->first].name, "p")); } else { // the corresponding vector parameter is not the return parameter, or it's not a two-step algorithm if (singular) { // for the singular version, the count is just 1. os << "1 "; } else { // for the non-singular version, the count is the size of the vector parameter // -> use the vector parameter name without leading 'p' to get the size (in number of elements, not in bytes) os << startLowerCase(strip(commandData.params[it->second].name, "p")) << ".size() "; } if (commandData.templateParam == it->second) { // if the vector parameter is templatized -> multiply by the size of that type to get the size in bytes if (commandData.enhancedReturnType != "std::vector") { os << "* sizeof( T ) "; } } } } void VulkanHppGenerator::writeCallPlainTypeParameter(std::ostream & os, ParamData const& paramData) { // this parameter is just a plain type if (paramData.type.back() == '*') { // it's a pointer std::string parameterName = startLowerCase(strip(paramData.name, "p")); if (paramData.type.find("const") != std::string::npos) { // it's a const pointer if (paramData.pureType == "char") { // it's a const pointer to char -> it's a string -> get the data via c_str() os << parameterName; if (paramData.optional) { // it's optional -> might use nullptr os << " ? " << parameterName << "->c_str() : nullptr"; } else { os << ".c_str()"; } } else { // it's const pointer to something else -> just use the name assert(!paramData.optional); os << paramData.name; } } else { // it's a non-const pointer, and char is the only type that occurs -> use the address of the parameter assert(paramData.type.find("char") == std::string::npos); os << "&" << parameterName; } } else { // it's a plain parameter -> just use its name os << paramData.name; } } void VulkanHppGenerator::writeCallVectorParameter(std::ostream & os, CommandData const& commandData, bool firstCall, bool singular, std::map::const_iterator it) { // this parameter is a vector parameter assert(commandData.params[it->first].type.back() == '*'); if ((commandData.returnParam == it->first) && commandData.twoStep && firstCall) { // this parameter is the return parameter, and it's the first call of a two-step algorithm -> just just nullptr os << "nullptr"; } else { std::string parameterName = startLowerCase(strip(commandData.params[it->first].name, "p")); std::set::const_iterator vkit = m_vkTypes.find(commandData.params[it->first].pureType); if ((vkit != m_vkTypes.end()) || (it->first == commandData.templateParam)) { // CHECK for !commandData.params[it->first].optional // this parameter is a vulkan type or a templated type -> need to reinterpret cast writeReinterpretCast(os, commandData.params[it->first].type.find("const") == 0, vkit != m_vkTypes.end(), commandData.params[it->first].pureType, commandData.params[it->first].type.rfind("* const") != std::string::npos); os << "( "; if (singular) { // in singular case, strip the plural-S from the name, and use the pointer to that thing os << "&" << stripPluralS(parameterName); } else { // in plural case, get the pointer to the data os << parameterName << ".data()"; } os << " )"; } else if (commandData.params[it->first].pureType == "char") { // the parameter is a vector to char -> it might be optional // besides that, the parameter now is a std::string -> get the pointer via c_str() os << parameterName; if (commandData.params[it->first].optional) { os << " ? " << parameterName << "->c_str() : nullptr"; } else { os << ".c_str()"; } } else { // this parameter is just a vetor -> get the pointer to its data os << parameterName << ".data()"; } } } void VulkanHppGenerator::writeCallVulkanTypeParameter(std::ostream & os, ParamData const& paramData) { // this parameter is a vulkan type if (paramData.type.back() == '*') { // it's a pointer -> needs a reinterpret cast to the vulkan type std::string parameterName = startLowerCase(strip(paramData.name, "p")); writeReinterpretCast(os, paramData.type.find("const") != std::string::npos, true, paramData.pureType, false); os << "( "; if (paramData.optional) { // for an optional parameter, we need also a static_cast from optional type to const-pointer to pure type os << "static_cast( " << parameterName << " )"; } else { // other parameters can just use the pointer os << "&" << parameterName; } os << " )"; } else { // a non-pointer parameter needs a static_cast from vk::-type to vulkan type os << "static_cast( " << paramData.name << " )"; } } void VulkanHppGenerator::writeEnumsToString(std::ostream & os, EnumData const& enumData) { // the helper functions to make strings out of enum values enterProtect(os, enumData.protect); os << " VULKAN_HPP_INLINE std::string to_string(" << enumData.name << (enumData.values.empty() ? ")" : " value)") << std::endl << " {" << std::endl; if (enumData.values.empty()) { // no enum values in this enum -> return "(void)" os << " return \"(void)\";" << std::endl; } else { // otherwise switch over the value and return the a stringized version of that value (without leading 'e') os << " switch (value)" << std::endl << " {" << std::endl; for (auto const& value : enumData.values) { os << " case " << enumData.name << "::" << value.name << ": return \"" << value.name.substr(1) << "\";" << std::endl; } os << " default: return \"invalid\";" << std::endl << " }" << std::endl; } os << " }" << std::endl; leaveProtect(os, enumData.protect); os << std::endl; } // Intended only for `enum class Result`! void VulkanHppGenerator::writeExceptionsForEnum(std::ostream & os, EnumData const& enumData) { std::string templateString = R"( class ${className} : public SystemError { public: ${className}( std::string const& message ) : SystemError( make_error_code( ${enumName}::${enumMemberName} ), message ) {} ${className}( char const * message ) : SystemError( make_error_code( ${enumName}::${enumMemberName} ), message ) {} }; )"; enterProtect(os, enumData.protect); for (size_t i = 0; i < enumData.values.size(); i++) { if (!isErrorEnum(enumData.values[i].name)) { continue; } os << replaceWithMap(templateString, { { "className", stripErrorEnumPrefix(enumData.values[i].name) + "Error" }, { "enumName", enumData.name }, { "enumMemberName", enumData.values[i].name } }); } leaveProtect(os, enumData.protect); os << std::endl; } void VulkanHppGenerator::writeFunction(std::ostream & os, std::string const& indentation, CommandData const& commandData, bool definition, bool enhanced, bool singular, bool unique, bool isStructureChain) { writeFunctionHeaderTemplate(os, indentation, commandData, enhanced, unique, !definition, isStructureChain); os << indentation << (definition ? "VULKAN_HPP_INLINE " : ""); writeFunctionHeaderReturnType(os, commandData, enhanced, singular, unique, isStructureChain); if (definition && !commandData.className.empty()) { os << commandData.className << "::"; } writeFunctionHeaderName(os, commandData.reducedName, singular, unique); writeFunctionHeaderArguments(os, commandData, enhanced, singular, !definition); os << (definition ? "" : ";") << std::endl; if (definition) { // write the function body os << indentation << "{" << std::endl; if (enhanced) { writeFunctionBodyEnhanced(os, indentation, commandData, singular, unique, isStructureChain); } else { writeFunctionBodyStandard(os, indentation, commandData); } os << indentation << "}" << std::endl; } } void VulkanHppGenerator::writeFunctionBodyEnhanced(std::ostream & os, std::string const& indentation, CommandData const& commandData, bool singular, bool unique, bool isStructureChain) { if (unique && !singular && (commandData.vectorParams.find(commandData.returnParam) != commandData.vectorParams.end())) // returns a vector of UniqueStuff { std::string const stringTemplate = R"(${i} static_assert( sizeof( ${type} ) <= sizeof( Unique${type} ), "${type} is greater than Unique${type}!" ); ${i} std::vector ${typeVariable}s; ${i} ${typeVariable}s.reserve( ${vectorSize} ); ${i} ${type}* buffer = reinterpret_cast<${type}*>( reinterpret_cast( ${typeVariable}s.data() ) + ${vectorSize} * ( sizeof( Unique${type} ) - sizeof( ${type} ) ) ); ${i} Result result = static_cast(d.vk${command}( m_device, ${arguments}, reinterpret_cast( buffer ) ) ); ${i} ${Deleter}<${DeleterTemplate},Dispatch> deleter( *this, ${deleterArg}, d ); ${i} for ( size_t i=0 ; i<${vectorSize} ; i++ ) ${i} { ${i} ${typeVariable}s.push_back( Unique${type}( buffer[i], deleter ) ); ${i} } ${i} return createResultValue( result, ${typeVariable}s, VULKAN_HPP_NAMESPACE_STRING "::${class}::${function}Unique" ); )"; std::string type = (commandData.returnParam != ~0) ? commandData.params[commandData.returnParam].pureType : ""; std::string typeVariable = startLowerCase(type); std::ostringstream arguments; writeArguments(arguments, commandData, true, singular, 1, commandData.params.size() - 1); std::map::const_iterator ddit = m_deleters.find(type); assert(ddit != m_deleters.end()); bool isCreateFunction = (commandData.fullName.substr(0, 6) == "create"); os << replaceWithMap(stringTemplate, std::map { { "i", indentation }, { "type", type }, { "typeVariable", typeVariable }, { "vectorSize", isCreateFunction ? "createInfos.size()" : "allocateInfo." + typeVariable + "Count" }, { "command", startUpperCase(commandData.fullName) }, { "arguments", arguments.str() }, { "Deleter", ddit->second.pool.empty() ? "ObjectDestroy" : "PoolFree" }, { "DeleterTemplate", ddit->second.pool.empty() ? commandData.className : commandData.className + "," + ddit->second.pool }, { "deleterArg", ddit->second.pool.empty() ? "allocator" : "allocateInfo." + startLowerCase(ddit->second.pool) }, { "class", commandData.className }, { "function", commandData.reducedName } }); } else { if (1 < commandData.vectorParams.size()) { writeFunctionBodyEnhancedMultiVectorSizeCheck(os, indentation, commandData); } std::string returnName; if (commandData.returnParam != ~0) { returnName = writeFunctionBodyEnhancedLocalReturnVariable(os, indentation, commandData, singular, isStructureChain); } if (commandData.twoStep) { assert(!singular); writeFunctionBodyEnhancedLocalCountVariable(os, indentation, commandData); // we now might have to check the result, resize the returned vector accordingly, and call the function again std::map::const_iterator returnit = commandData.vectorParams.find(commandData.returnParam); assert(returnit != commandData.vectorParams.end() && (returnit->second != ~0)); std::string sizeName = startLowerCase(strip(commandData.params[returnit->second].name, "p")); if (commandData.returnType == "Result") { if (1 < commandData.successCodes.size()) { writeFunctionBodyEnhancedCallTwoStepIterate(os, indentation, returnName, sizeName, commandData); } else { writeFunctionBodyEnhancedCallTwoStepChecked(os, indentation, returnName, sizeName, commandData); } } else { writeFunctionBodyEnhancedCallTwoStep(os, indentation, returnName, sizeName, commandData); } } else { if (commandData.returnType == "Result") { writeFunctionBodyEnhancedCallResult(os, indentation, commandData, singular); } else { writeFunctionBodyEnhancedCall(os, indentation, commandData, singular); } } if ((commandData.returnType == "Result") || !commandData.successCodes.empty()) { writeFunctionBodyEnhancedReturnResultValue(os, indentation, returnName, commandData, singular, unique); } else if ((commandData.returnParam != ~0) && (commandData.returnType != commandData.enhancedReturnType)) { // for the other returning cases, when the return type is somhow enhanced, just return the local returnVariable os << indentation << " return " << returnName << ";" << std::endl; } } } void VulkanHppGenerator::writeFunctionBodyEnhanced(std::ostream &os, std::string const& templateString, std::string const& indentation, CommandData const& commandData, bool singular) { os << replaceWithMap(templateString, { { "call", generateCall(commandData, true, singular) }, { "i", indentation } }); } void VulkanHppGenerator::writeFunctionBodyTwoStep(std::ostream & os, std::string const &templateString, std::string const& indentation, std::string const& returnName, std::string const& sizeName, CommandData const& commandData) { std::map replacements = { { "sizeName", sizeName }, { "returnName", returnName }, { "call1", generateCall(commandData, true, false) }, { "call2", generateCall(commandData, false, false) }, { "i", indentation } }; os << replaceWithMap(templateString, replacements); } std::string VulkanHppGenerator::writeFunctionBodyEnhancedLocalReturnVariable(std::ostream & os, std::string const& indentation, CommandData const& commandData, bool singular, bool isStructureChain) { std::string returnName = startLowerCase(strip(commandData.params[commandData.returnParam].name, "p")); // there is a returned parameter -> we need a local variable to hold that value if (commandData.returnType != commandData.enhancedReturnType) { // the returned parameter is somehow enhanced by us os << indentation << " "; if (singular) { if (isStructureChain) { std::string const &pureType = commandData.params[commandData.returnParam].pureType; // For StructureChains use the template parameters os << "StructureChain structureChain;" << std::endl; returnName = stripPluralS(returnName); os << indentation << " " << pureType << "& " << returnName << " = structureChain.template get<" << pureType << ">()"; returnName = "structureChain"; } else { // in singular case, just use the return parameters pure type for the return variable returnName = stripPluralS(returnName); os << commandData.params[commandData.returnParam].pureType << " " << returnName; } } else { // in non-singular case, use the enhanced type for the return variable (like vector<...>) if (isStructureChain) { std::string const &returnType = commandData.enhancedReturnType; // For StructureChains use the template parameters os << "StructureChain structureChain;" << std::endl; os << indentation << " " << returnType << "& " << returnName << " = structureChain.template get<" << returnType << ">()"; returnName = "structureChain"; } else { os << commandData.enhancedReturnType << " " << returnName; } std::map::const_iterator it = commandData.vectorParams.find(commandData.returnParam); if (it != commandData.vectorParams.end() && !commandData.twoStep) { // if the return parameter is a vector parameter, and not part of a two-step algorithm, initialize its size std::string size; if (it->second == ~0) { assert(!commandData.params[commandData.returnParam].len.empty()); // the size of the vector is not given by an other parameter, but by some member of a parameter, described as 'parameter::member' // -> replace the '::' by '.' and filter out the leading 'p' to access that value size = startLowerCase(strip(commandData.params[commandData.returnParam].len, "p")); size_t pos = size.find("::"); assert(pos != std::string::npos); size.replace(pos, 2, "."); } else { // the size of the vector is given by an other parameter // first check, if that size has become the size of some other vector parameter // -> look for it and get it's actual size for (auto const& vectorParam : commandData.vectorParams) { if ((vectorParam.first != it->first) && (vectorParam.second == it->second)) { size = startLowerCase(strip(commandData.params[vectorParam.first].name, "p")) + ".size()"; break; } } if (size.empty()) { // otherwise, just use that parameter size = commandData.params[it->second].name; } } assert(!size.empty()); os << "( " << size << " )"; } } os << ";" << std::endl; } else { // the return parameter is not enhanced -> the type is supposed to be a Result and there are more than one success codes! assert((commandData.returnType == "Result") && (1 < commandData.successCodes.size())); os << indentation << " " << commandData.params[commandData.returnParam].pureType << " " << returnName << ";" << std::endl; } return returnName; } void VulkanHppGenerator::writeFunctionBodyEnhancedCall(std::ostream &os, std::string const& indentation, CommandData const& commandData, bool singular) { std::string const templateString = "${i} return ${call};\n"; std::string const templateStringVoid = "${i} ${call};\n"; writeFunctionBodyEnhanced(os, commandData.returnType == "void" ? templateStringVoid : templateString, indentation, commandData, singular); } void VulkanHppGenerator::writeFunctionBodyEnhancedCallResult(std::ostream &os, std::string const& indentation, CommandData const& commandData, bool singular) { std::string const templateString = "${i} Result result = static_cast( ${call} );\n"; writeFunctionBodyEnhanced(os, templateString, indentation, commandData, singular); } void VulkanHppGenerator::writeFunctionBodyEnhancedCallTwoStep(std::ostream & os, std::string const& indentation, std::string const& returnName, std::string const& sizeName, CommandData const& commandData) { std::string const templateString = R"(${i} ${call1}; ${i} ${returnName}.resize( ${sizeName} ); ${i} ${call2}; )"; writeFunctionBodyTwoStep(os, templateString, indentation, returnName, sizeName, commandData); } void VulkanHppGenerator::writeFunctionBodyEnhancedCallTwoStepIterate(std::ostream & os, std::string const& indentation, std::string const& returnName, std::string const& sizeName, CommandData const& commandData) { std::string const templateString = R"(${i} Result result; ${i} do ${i} { ${i} result = static_cast( ${call1} ); ${i} if ( ( result == Result::eSuccess ) && ${sizeName} ) ${i} { ${i} ${returnName}.resize( ${sizeName} ); ${i} result = static_cast( ${call2} ); ${i} } ${i} } while ( result == Result::eIncomplete ); ${i} VULKAN_HPP_ASSERT( ${sizeName} <= ${returnName}.size() ); ${i} ${returnName}.resize( ${sizeName} ); )"; writeFunctionBodyTwoStep(os, templateString, indentation, returnName, sizeName, commandData); } void VulkanHppGenerator::writeFunctionBodyEnhancedCallTwoStepChecked(std::ostream & os, std::string const& indentation, std::string const& returnName, std::string const& sizeName, CommandData const& commandData) { std::string const templateString = R"(${i} Result result = static_cast( ${call1} ); ${i} if ( ( result == Result::eSuccess ) && ${sizeName} ) ${i} { ${i} ${returnName}.resize( ${sizeName} ); ${i} result = static_cast( ${call2} ); ${i} } )"; writeFunctionBodyTwoStep(os, templateString, indentation, returnName, sizeName, commandData); } void VulkanHppGenerator::writeFunctionBodyEnhancedLocalCountVariable(std::ostream & os, std::string const& indentation, CommandData const& commandData) { // local count variable to hold the size of the vector to fill assert(commandData.returnParam != ~0); std::map::const_iterator returnit = commandData.vectorParams.find(commandData.returnParam); assert(returnit != commandData.vectorParams.end() && (returnit->second != ~0)); assert((commandData.returnType == "Result") || (commandData.returnType == "void")); // take the pure type of the size parameter; strip the leading 'p' from its name for its local name os << indentation << " " << commandData.params[returnit->second].pureType << " " << startLowerCase(strip(commandData.params[returnit->second].name, "p")) << ";" << std::endl; } void VulkanHppGenerator::writeFunctionBodyEnhancedMultiVectorSizeCheck(std::ostream & os, std::string const& indentation, CommandData const& commandData) { std::string const templateString = R"#(#ifdef VULKAN_HPP_NO_EXCEPTIONS ${i} VULKAN_HPP_ASSERT( ${firstVectorName}.size() == ${secondVectorName}.size() ); #else ${i} if ( ${firstVectorName}.size() != ${secondVectorName}.size() ) ${i} { ${i} throw LogicError( VULKAN_HPP_NAMESPACE_STRING "::${className}::${reducedName}: ${firstVectorName}.size() != ${secondVectorName}.size()" ); ${i} } #endif // VULKAN_HPP_NO_EXCEPTIONS )#"; // add some error checks if multiple vectors need to have the same size for (std::map::const_iterator it0 = commandData.vectorParams.begin(); it0 != commandData.vectorParams.end(); ++it0) { if (it0->first != commandData.returnParam) { for (std::map::const_iterator it1 = std::next(it0); it1 != commandData.vectorParams.end(); ++it1) { if ((it1->first != commandData.returnParam) && (it0->second == it1->second)) { os << replaceWithMap(templateString, std::map({ { "firstVectorName", startLowerCase(strip(commandData.params[it0->first].name, "p")) }, { "secondVectorName", startLowerCase(strip(commandData.params[it1->first].name, "p")) }, { "className", commandData.className }, { "reducedName", commandData.reducedName }, { "i", indentation } })); } } } } } void VulkanHppGenerator::writeFunctionBodyEnhancedReturnResultValue(std::ostream & os, std::string const& indentation, std::string const& returnName, CommandData const& commandData, bool singular, bool unique) { std::string type = (commandData.returnParam != ~0) ? commandData.params[commandData.returnParam].pureType : ""; std::string returnVectorName = (commandData.returnParam != ~0) ? strip(commandData.params[commandData.returnParam].name, "p", "s") : ""; if (unique) { // the unique version needs a Deleter object for destruction of the newly created stuff // get the DeleterData corresponding to the returned type std::map::const_iterator ddit = m_deleters.find(type); assert(ddit != m_deleters.end() && ddit->second.pool.empty()); // special handling for "createDevice", as Device is created from PhysicalDevice, but destroyed on its own bool noParent = commandData.className.empty() || (commandData.fullName == "createDevice"); os << std::endl << indentation << ((commandData.fullName == "allocateMemory") ? " ObjectFree<" : " ObjectDestroy<") << (noParent ? "NoParent" : commandData.className) << ",Dispatch> deleter( " << (noParent ? "" : "*this, ") << "allocator, d );" << std::endl << indentation << " return createResultValue<" << type << ",Dispatch>( result, "; } else { os << indentation << " return createResultValue( result, "; } // if the return type is "Result" or there is at least one success code, create the Result/Value construct to return if (commandData.returnParam != ~0) { // if there's a return parameter, list it in the Result/Value constructor os << returnName << ", "; } // now the function name (with full namespace) as a string os << "VULKAN_HPP_NAMESPACE_STRING\"::" << (commandData.className.empty() ? "" : commandData.className + "::") << (singular ? stripPluralS(commandData.reducedName) : commandData.reducedName) << (unique ? "Unique" : "") << "\""; if (!commandData.twoStep && (1 < commandData.successCodes.size())) { // and for the single-step algorithms with more than one success code list them all os << ", { Result::" << commandData.successCodes[0]; for (size_t i = 1; i < commandData.successCodes.size(); i++) { os << ", Result::" << commandData.successCodes[i]; } os << " }"; } if (unique) { os << ", deleter"; } os << " );" << std::endl; } void VulkanHppGenerator::writeFunctionBodyStandard(std::ostream & os, std::string const& indentation, CommandData const& commandData) { os << indentation << " "; bool castReturn = false; if (commandData.returnType != "void") { // there's something to return... os << "return "; castReturn = (m_vkTypes.find(commandData.returnType) != m_vkTypes.end()); if (castReturn) { // the return-type is a vulkan type -> need to cast to vk::-type os << "static_cast<" << commandData.returnType << ">( "; } } // call the original function os << "d.vk" << startUpperCase(commandData.fullName) << "( "; if (!commandData.className.empty()) { // the command is part of a class -> the first argument is the member variable, starting with "m_" assert(commandData.className == commandData.params[0].type); os << "m_" << startLowerCase(commandData.className); } // list all the arguments for (size_t i = commandData.className.empty() ? 0 : 1; i < commandData.params.size(); i++) { if (0 < i) { os << ", "; } if (m_vkTypes.find(commandData.params[i].pureType) != m_vkTypes.end()) { // the parameter is a vulkan type if (commandData.params[i].type.back() == '*') { // it's a pointer -> need to reinterpret_cast it writeReinterpretCast(os, commandData.params[i].type.find("const") == 0, true, commandData.params[i].pureType, commandData.params[i].type.find("* const") != std::string::npos); } else { // it's a value -> need to static_cast ist os << "static_cast"; } os << "( " << commandData.params[i].name << " )"; } else { // it's a non-vulkan type -> just use it os << commandData.params[i].name; } } os << " )"; if (castReturn) { // if we cast the return -> close the static_cast os << " )"; } os << ";" << std::endl; } void VulkanHppGenerator::writeFunctionHeaderArguments(std::ostream & os, CommandData const& commandData, bool enhanced, bool singular, bool withDefaults) { os << "("; if (enhanced) { writeFunctionHeaderArgumentsEnhanced(os, commandData, singular, withDefaults); } else { writeFunctionHeaderArgumentsStandard(os, commandData, withDefaults); } os << ")"; if (!commandData.className.empty()) { os << " const"; } } void VulkanHppGenerator::writeFunctionHeaderArgumentsEnhanced(std::ostream & os, CommandData const& commandData, bool singular, bool withDefaults) { // check if there's at least one argument left to put in here if (commandData.skippedParams.size() + (commandData.className.empty() ? 0 : 1) < commandData.params.size()) { // determine the last argument, where we might provide some default for size_t lastArgument = size_t(~0); for (size_t i = commandData.params.size() - 1; i < commandData.params.size(); i--) { if (commandData.skippedParams.find(i) == commandData.skippedParams.end()) { lastArgument = i; break; } } os << " "; bool argEncountered = false; for (size_t i = commandData.className.empty() ? 0 : 1; i < commandData.params.size(); i++) { if (commandData.skippedParams.find(i) == commandData.skippedParams.end()) { if (argEncountered) { os << ", "; } std::string strippedParameterName = startLowerCase(strip(commandData.params[i].name, "p")); std::map::const_iterator it = commandData.vectorParams.find(i); size_t rightStarPos = commandData.params[i].type.rfind('*'); if (it == commandData.vectorParams.end()) { // the argument ist not a vector if (rightStarPos == std::string::npos) { // and its not a pointer -> just use its type and name here os << commandData.params[i].type << " " << commandData.params[i].name; if (!commandData.params[i].arraySize.empty()) { os << "[" << commandData.params[i].arraySize << "]"; } if (withDefaults && (lastArgument == i)) { // check if the very last argument is a flag without any bits -> provide some empty default for it std::map::const_iterator bitmasksIt = m_bitmasks.find(commandData.params[i].pureType); if (bitmasksIt != m_bitmasks.end()) { // get the enum corresponding to this flag, to check if it's empty std::list::const_iterator depIt = std::find_if(m_dependencies.begin(), m_dependencies.end(), [&bitmasksIt](DependencyData const& dd) { return(dd.name == bitmasksIt->first); }); assert((depIt != m_dependencies.end()) && (depIt->dependencies.size() == 1)); std::map::const_iterator enumIt = m_enums.find(*depIt->dependencies.begin()); assert(enumIt != m_enums.end()); if (enumIt->second.values.empty()) { // there are no bits in this flag -> provide the default os << " = " << commandData.params[i].pureType << "()"; } } } } else { // the argument is not a vector, but a pointer assert(commandData.params[i].type[rightStarPos] == '*'); if (commandData.params[i].optional) { // for an optional argument, trim the trailing '*' from the type, and the leading 'p' from the name os << "Optional<" << trimEnd(commandData.params[i].type.substr(0, rightStarPos)) << "> " << strippedParameterName; if (withDefaults) { os << " = nullptr"; } } else if (commandData.params[i].pureType == "void") { // for void-pointer, just use type and name os << commandData.params[i].type << " " << commandData.params[i].name; } else if (commandData.params[i].pureType != "char") { // for non-char-pointer, change to reference os << trimEnd(commandData.params[i].type.substr(0, rightStarPos)) << " & " << strippedParameterName; } else { // for char-pointer, change to const reference to std::string os << "const std::string & " << strippedParameterName; } } } else { // the argument is a vector // it's optional, if it's marked as optional and there's no size specified bool optional = commandData.params[i].optional && (it->second == ~0); assert((rightStarPos != std::string::npos) && (commandData.params[i].type[rightStarPos] == '*')); if (commandData.params[i].type.find("char") != std::string::npos) { // it's a char-vector -> use a std::string (either optional or a const-reference if (optional) { os << "Optional " << strippedParameterName; if (withDefaults) { os << " = nullptr"; } } else { os << "const std::string & " << strippedParameterName; } } else { // it's a non-char vector (they are never optional) assert(!optional); if (singular) { // in singular case, change from pointer to reference os << trimEnd(commandData.params[i].type.substr(0, rightStarPos)) << " & " << stripPluralS(strippedParameterName); } else { // otherwise, use our ArrayProxy bool isConst = (commandData.params[i].type.find("const") != std::string::npos); os << "ArrayProxy<" << ((commandData.templateParam == i) ? (isConst ? "const T" : "T") : trimEnd(commandData.params[i].type.substr(0, rightStarPos))) << "> " << strippedParameterName; } } } argEncountered = true; } } if (argEncountered) { os << ", "; } } os << "Dispatch const &d"; if (withDefaults) { os << " = Dispatch()"; } os << " "; } void VulkanHppGenerator::writeFunctionHeaderArgumentsStandard(std::ostream & os, CommandData const& commandData, bool withDefaults) { // for the standard case, just list all the arguments as we've got them bool argEncountered = false; // determine the last argument, where we might provide some default for size_t lastArgument = commandData.params.size() - 1; for (size_t i = commandData.className.empty() ? 0 : 1; i < commandData.params.size(); i++) { if (argEncountered) { os << ","; } os << " " << commandData.params[i].type << " " << commandData.params[i].name; if (!commandData.params[i].arraySize.empty()) { os << "[" << commandData.params[i].arraySize << "]"; } if (withDefaults && (lastArgument == i)) { // check if the very last argument is a flag without any bits -> provide some empty default for it std::map::const_iterator flagIt = m_bitmasks.find(commandData.params[i].pureType); if (flagIt != m_bitmasks.end()) { // get the enum corresponding to this flag, to check if it's empty std::list::const_iterator depIt = std::find_if(m_dependencies.begin(), m_dependencies.end(), [&flagIt](DependencyData const& dd) { return(dd.name == flagIt->first); }); assert((depIt != m_dependencies.end()) && (depIt->dependencies.size() == 1)); std::map::const_iterator enumIt = m_enums.find(*depIt->dependencies.begin()); assert(enumIt != m_enums.end()); if (enumIt->second.values.empty()) { // there are no bits in this flag -> provide the default os << " = " << commandData.params[i].pureType << "()"; } } } argEncountered = true; } if (argEncountered) { os << ", "; } os << "Dispatch const &d"; if (withDefaults) { os << " = Dispatch() "; } } void VulkanHppGenerator::writeFunctionHeaderReturnType(std::ostream & os, CommandData const& commandData, bool enhanced, bool singular, bool unique, bool isStructureChain) { std::string templateString; std::string returnType; if (enhanced) { // the enhanced function might return some pretty complex return stuff if (isStructureChain || (!singular && (commandData.enhancedReturnType.find("Allocator") != std::string::npos))) { // for the non-singular case with allocation, we need to prepend with 'typename' to keep compilers happy templateString = "typename "; } if (unique) { // the unique version returns something prefixed with 'Unique'; potentially a vector of that stuff // it's a vector, if it's not the singular version and the return parameter is a vector parameter bool returnsVector = !singular && (commandData.vectorParams.find(commandData.returnParam) != commandData.vectorParams.end()); templateString += returnsVector ? "ResultValueType,Allocator>>::type " : "typename ResultValueType>::type "; returnType = isStructureChain ? "StructureChain" : commandData.params[commandData.returnParam].pureType; } else if ((commandData.enhancedReturnType != commandData.returnType) && (commandData.returnType != "void")) { // if the enhanced return type differs from the original return type, and it's not void, we return a ResultValueType<...>::type templateString += "ResultValueType<${returnType}>::type "; assert(commandData.returnType == "Result"); // in singular case, we create the ResultValueType from the pure return type, otherwise from the enhanced return type if (isStructureChain) { returnType = "StructureChain"; } else { returnType = singular ? commandData.params[commandData.returnParam].pureType : commandData.enhancedReturnType; } } else if ((commandData.returnParam != ~0) && (1 < commandData.successCodes.size())) { // if there is a return parameter at all, and there are multiple success codes, we return a ResultValue<...> with the pure return type assert(commandData.returnType == "Result"); templateString = "ResultValue<${returnType}> "; returnType = isStructureChain ? "StructureChain" : commandData.params[commandData.returnParam].pureType; } else { // and in every other case, we just return the enhanced return type. templateString = "${returnType} "; returnType = isStructureChain ? "StructureChain" : commandData.enhancedReturnType; } } else { // the non-enhanced function just uses the return type templateString = "${returnType} "; returnType = commandData.returnType; } os << replaceWithMap(templateString, { { "returnType", returnType } }); } void VulkanHppGenerator::writeFunctionHeaderTemplate(std::ostream & os, std::string const& indentation, CommandData const& commandData, bool enhanced, bool unique, bool withDefault, bool isStructureChain) { std::string dispatch = withDefault ? std::string("typename Dispatch = DispatchLoaderStatic") : std::string("typename Dispatch"); if (enhanced && isStructureChain) { os << indentation << "template " << std::endl; } else if (enhanced && (commandData.templateParam != ~0) && ((commandData.templateParam != commandData.returnParam) || (commandData.enhancedReturnType == "Result"))) { // if there's a template parameter, not being the return parameter or where the enhanced return type is 'Result' -> templatize on type 'T' assert(commandData.enhancedReturnType.find("Allocator") == std::string::npos); os << indentation << "template " << std::endl; } else if (enhanced && (commandData.enhancedReturnType.find("Allocator") != std::string::npos)) { // otherwise, if there's an Allocator used in the enhanced return type, we templatize on that Allocator assert((commandData.enhancedReturnType.substr(0, 12) == "std::vector<") && (commandData.enhancedReturnType.find(',') != std::string::npos) && (12 < commandData.enhancedReturnType.find(','))); os << indentation << "template ' os << " = std::allocator<" << (unique ? "Unique" : "") << commandData.enhancedReturnType.substr(12, commandData.enhancedReturnType.find(',') - 12) << ">"; } os << ", " << dispatch; os << "> " << std::endl; } else { os << indentation << "template<" << dispatch << ">" << std::endl; } } void VulkanHppGenerator::writeResultEnum(std::ostream & os) { std::list::const_iterator it = std::find_if(m_dependencies.begin(), m_dependencies.end(), [](DependencyData const& dp) { return dp.name == "Result"; }); assert(it != m_dependencies.end()); writeTypeEnum(os, m_enums.find(it->name)->second); writeEnumsToString(os, m_enums.find(it->name)->second); os << "#ifndef VULKAN_HPP_NO_EXCEPTIONS"; os << exceptionHeader; os << exceptionClassesHeader; writeExceptionsForEnum(os, m_enums.find(it->name)->second); writeThrowExceptions(os, m_enums.find(it->name)->second); os << "#endif" << std::endl; m_dependencies.erase(it); } void VulkanHppGenerator::writeStructConstructor(std::ostream & os, std::string const& name, StructData const& structData, std::map const& defaultValues) { // the constructor with all the elements as arguments, with defaults std::string ctorOpening = " " + name + "( "; size_t indentSize = ctorOpening.size(); os << ctorOpening; bool listedArgument = false; for (size_t i = 0; i < structData.members.size(); i++) { listedArgument = writeStructConstructorArgument(os, listedArgument, indentSize, structData.members[i], defaultValues); } os << " )" << std::endl; // copy over the simple arguments bool firstArgument = true; for (size_t i = 0; i < structData.members.size(); i++) { // skip members 'pNext' and 'sType' are directly set by initializers if ((structData.members[i].name != "pNext") && (structData.members[i].name != "sType") && (structData.members[i].arraySize.empty())) { // here, we can only handle non-array arguments std::string templateString = " ${sep} ${member}( ${value} )\n"; std::string sep = firstArgument ? ":" : ","; std::string member = structData.members[i].name; std::string value = structData.members[i].name + "_"; // the elements are initialized by the corresponding argument (with trailing '_', as mentioned above) os << replaceWithMap(templateString, { { "sep", sep },{ "member", member },{ "value", value } }); firstArgument = false; } } // the body of the constructor, copying over data from argument list into wrapped struct os << " {" << std::endl; for (size_t i = 0; i < structData.members.size(); i++) { if (!structData.members[i].arraySize.empty()) { // here we can handle the arrays, copying over from argument (with trailing '_') to member // size is arraySize times sizeof type std::string member = structData.members[i].name; std::string arraySize = structData.members[i].arraySize; std::string type = structData.members[i].type; os << replaceWithMap(" memcpy( &${member}, ${member}_.data(), ${arraySize} * sizeof( ${type} ) );\n", { { "member", member },{ "arraySize", arraySize },{ "type", type } }); } } os << " }\n\n"; if (!structData.subStruct.empty()) { auto const& subStruct = m_structs.find(structData.subStruct); assert(subStruct != m_structs.end()); std::string subStructArgumentName = startLowerCase(strip(subStruct->first, "vk")); ctorOpening = " explicit " + name + "( "; indentSize = ctorOpening.size(); os << ctorOpening << subStruct->first << " const& " << subStructArgumentName; for (size_t i = subStruct->second.members.size(); i < structData.members.size(); i++) { writeStructConstructorArgument(os, true, indentSize, structData.members[i], defaultValues); } os << " )" << std::endl; firstArgument = true; std::string templateString = " ${sep} ${member}( ${value} )\n"; for (size_t i = 0; i < subStruct->second.members.size(); i++) { assert(structData.members[i].arraySize.empty()); std::string sep = firstArgument ? ":" : ","; std::string member = structData.members[i].name; std::string value = subStructArgumentName + "." + subStruct->second.members[i].name; os << replaceWithMap(templateString, { { "sep", sep },{ "member", member },{ "value", value } }); firstArgument = false; } for (size_t i = subStruct->second.members.size(); i < structData.members.size(); i++) { assert(structData.members[i].arraySize.empty()); std::string member = structData.members[i].name; std::string value = structData.members[i].name + "_"; // the elements are initialized by the corresponding argument (with trailing '_', as mentioned above) os << replaceWithMap(templateString, { { "sep", "," },{ "member", member },{ "value", value } }); } os << " {}" << std::endl << std::endl; } std::string templateString = R"( ${name}( Vk${name} const & rhs ) { memcpy( this, &rhs, sizeof( ${name} ) ); } ${name}& operator=( Vk${name} const & rhs ) { memcpy( this, &rhs, sizeof( ${name} ) ); return *this; } )"; os << replaceWithMap(templateString, { { "name", name } }); } void VulkanHppGenerator::writeIndentation(std::ostream & os, size_t indentLength) { for(size_t i = 0; i < indentLength; i++) { os << " "; } } bool VulkanHppGenerator::writeStructConstructorArgument(std::ostream & os, bool listedArgument, size_t indentLength, MemberData const& memberData, std::map const& defaultValues) { if (listedArgument) { os << ",\n"; writeIndentation(os, indentLength); } // skip members 'pNext' and 'sType', as they are never explicitly set if ((memberData.name != "pNext") && (memberData.name != "sType")) { // find a default value for the given pure type std::map::const_iterator defaultIt = defaultValues.find(memberData.pureType); assert(defaultIt != defaultValues.end()); if (memberData.arraySize.empty()) { // the arguments name get a trailing '_', to distinguish them from the actual struct members // pointer arguments get a nullptr as default os << memberData.type << " " << memberData.name << "_ = " << (memberData.type.back() == '*' ? "nullptr" : defaultIt->second); } else { // array members are provided as const reference to a std::array // the arguments name get a trailing '_', to distinguish them from the actual struct members // list as many default values as there are elements in the array os << "std::array<" << memberData.type << "," << memberData.arraySize << "> const& " << memberData.name << "_ = { { " << defaultIt->second; size_t n = atoi(memberData.arraySize.c_str()); assert(0 < n); for (size_t j = 1; j < n; j++) { os << ", " << defaultIt->second; } os << " } }"; } listedArgument = true; } return listedArgument; } void VulkanHppGenerator::writeStructSetter(std::ostream & os, std::string const& structureName, MemberData const& memberData) { if (memberData.type != "StructureType") // filter out StructureType, which is supposed to be immutable ! { // the setters return a reference to the structure os << " " << structureName << "& set" << startUpperCase(memberData.name) << "( "; if (memberData.arraySize.empty()) { os << memberData.type << " "; } else { os << "std::array<" << memberData.type << "," << memberData.arraySize << "> "; } // add a trailing '_' to the argument to distinguish it from the structure member os << memberData.name << "_ )" << std::endl << " {" << std::endl; // copy over the argument, either by assigning simple data, or by memcpy array data if (memberData.arraySize.empty()) { os << " " << memberData.name << " = " << memberData.name << "_"; } else { os << " memcpy( &" << memberData.name << ", " << memberData.name << "_.data(), " << memberData.arraySize << " * sizeof( " << memberData.type << " ) )"; } os << ";" << std::endl << " return *this;" << std::endl << " }" << std::endl << std::endl; } } void VulkanHppGenerator::writeStructureChainValidation(std::ostream & os) { // write all template functions for the structure pointer chain validation for (auto it = m_dependencies.begin(); it != m_dependencies.end(); ++it) { switch (it->category) { case DependencyData::Category::STRUCT: writeStructureChainValidation(os, *it); break; } } } void VulkanHppGenerator::writeStructureChainValidation(std::ostream & os, DependencyData const& dependencyData) { std::map::const_iterator it = m_structs.find(dependencyData.name); assert(it != m_structs.end()); if (!it->second.structExtends.empty()) { enterProtect(os, it->second.protect); // write out allowed structure chains for (auto extendName : it->second.structExtends) { std::map::const_iterator itExtend = m_structs.find(extendName); if (itExtend == m_structs.end()) { std::stringstream errorString; errorString << extendName << " does not specify a struct in structextends field."; // check if symbol name is an alias to a struct auto itAlias = std::find_if(m_structs.begin(), m_structs.end(), [&extendName](std::pair const &it) -> bool {return it.second.alias == extendName;}); if (itAlias != m_structs.end()) { errorString << " The symbol is an alias and maps to " << itAlias->first << "."; } errorString << std::endl; throw std::runtime_error(errorString.str()); } enterProtect(os, itExtend->second.protect); os << " template <> struct isStructureChainValid<" << extendName << ", " << dependencyData.name << ">{ enum { value = true }; };" << std::endl; leaveProtect(os, itExtend->second.protect); } leaveProtect(os, it->second.protect); } } void VulkanHppGenerator::writeThrowExceptions(std::ostream & os, EnumData const& enumData) { enterProtect(os, enumData.protect); os << R"( VULKAN_HPP_INLINE void throwResultException( Result result, char const * message ) { switch ( result ) { )"; for (size_t i = 0; icategory) { case DependencyData::Category::BITMASK: writeBitmaskToString(os, it->name, m_enums.find(*it->dependencies.begin())->second); break; case DependencyData::Category::ENUM: assert(m_enums.find(it->name) != m_enums.end()); writeEnumsToString(os, m_enums.find(it->name)->second); break; } } } void VulkanHppGenerator::writeTypeBitmask(std::ostream & os, std::string const& bitmaskName, BitmaskData const& bitmaskData, EnumData const& enumData) { enterProtect(os, bitmaskData.protect); // each Flags class is using on the class 'Flags' with the corresponding FlagBits enum as the template parameter os << " using " << bitmaskName << " = Flags<" << enumData.name << ", Vk" << bitmaskName << ">;" << std::endl; std::stringstream allFlags; for (size_t i = 0; i < enumData.values.size(); i++) { if (i != 0) { allFlags << " | "; } allFlags << "VkFlags(" << enumData.name << "::" << enumData.values[i].name << ")"; } if (!enumData.values.empty()) { const std::string templateString = R"( VULKAN_HPP_INLINE ${bitmaskName} operator|( ${enumName} bit0, ${enumName} bit1 ) { return ${bitmaskName}( bit0 ) | bit1; } VULKAN_HPP_INLINE ${bitmaskName} operator~( ${enumName} bits ) { return ~( ${bitmaskName}( bits ) ); } template <> struct FlagTraits<${enumName}> { enum { allFlags = ${allFlags} }; }; )"; os << replaceWithMap(templateString, { { "bitmaskName", bitmaskName },{ "enumName", enumData.name },{ "allFlags", allFlags.str() } }); } if (!bitmaskData.alias.empty()) { os << std::endl << " using " << bitmaskData.alias << " = " << bitmaskName << ";" << std::endl; } leaveProtect(os, bitmaskData.protect); os << std::endl; } void VulkanHppGenerator::writeTypeCommand(std::ostream & os, DependencyData const& dependencyData) { assert(m_commands.find(dependencyData.name) != m_commands.end()); CommandData const& commandData = m_commands.find(dependencyData.name)->second; if (commandData.className.empty()) { if (commandData.fullName == "createInstance") { // special handling for createInstance, as we need to explicitly place the forward declarations and the deleter classes here #if !defined(NDEBUG) auto deleterTypesIt = m_deleterTypes.find(""); assert((deleterTypesIt != m_deleterTypes.end()) && (deleterTypesIt->second.size() == 2)); assert(deleterTypesIt->second.find("Instance") != deleterTypesIt->second.end()); #endif writeUniqueTypes(os, std::make_pair>("", { "Instance" })); writeTypeCommand(os, " ", commandData, false); } else { writeTypeCommand(os, " ", commandData, false); } writeTypeCommand(os, " ", commandData, true); os << std::endl; } } void VulkanHppGenerator::writeTypeCommand(std::ostream & os, std::string const& indentation, CommandData const& commandData, bool definition) { enterProtect(os, commandData.protect); bool isStructureChain = m_extendedStructs.find(commandData.enhancedReturnType) != m_extendedStructs.end(); // first create the standard version of the function std::ostringstream standard; writeFunction(standard, indentation, commandData, definition, false, false, false, false); // then the enhanced version, composed by up to five parts std::ostringstream enhanced; writeFunction(enhanced, indentation, commandData, definition, true, false, false, false); if (isStructureChain) { writeFunction(enhanced, indentation, commandData, definition, true, false, false, true); } // then a singular version, if a sized vector would be returned std::map::const_iterator returnVector = commandData.vectorParams.find(commandData.returnParam); bool singular = (returnVector != commandData.vectorParams.end()) && (returnVector->second != ~0) && (commandData.params[returnVector->first].pureType != "void") && (commandData.params[returnVector->second].type.back() != '*'); if (singular) { writeFunction(enhanced, indentation, commandData, definition, true, true, false, false); } // special handling for createDevice and createInstance ! bool specialWriteUnique = (commandData.reducedName == "createDevice") || (commandData.reducedName == "createInstance"); // and then the same for the Unique* versions (a Deleter is available for the commandData's class, and the function starts with 'allocate' or 'create') if (((m_deleters.find(commandData.className) != m_deleters.end()) || specialWriteUnique) && ((commandData.reducedName.substr(0, 8) == "allocate") || (commandData.reducedName.substr(0, 6) == "create"))) { enhanced << "#ifndef VULKAN_HPP_NO_SMART_HANDLE" << std::endl; writeFunction(enhanced, indentation, commandData, definition, true, false, true, false); if (singular) { writeFunction(enhanced, indentation, commandData, definition, true, true, true, false); } enhanced << "#endif /*VULKAN_HPP_NO_SMART_HANDLE*/" << std::endl; } // and write one or both of them writeStandardOrEnhanced(os, standard.str(), enhanced.str()); leaveProtect(os, commandData.protect); os << std::endl; } void VulkanHppGenerator::writeTypeEnum(std::ostream & os, EnumData const& enumData) { // a named enum per enum, listing all its values by setting them to the original Vulkan names enterProtect(os, enumData.protect); os << " enum class " << enumData.name << std::endl << " {" << std::endl; for (size_t i = 0; i list them first if (!dependencyData.forwardDependencies.empty()) { os << " // forward declarations" << std::endl; for (std::set::const_iterator it = dependencyData.forwardDependencies.begin(); it != dependencyData.forwardDependencies.end(); ++it) { assert(m_structs.find(*it) != m_structs.end()); os << " struct " << *it << ";" << std::endl; } os << std::endl; } // then write any forward declaration of Deleters used by this handle std::map>::const_iterator deleterTypesIt = m_deleterTypes.find(dependencyData.name); if (deleterTypesIt != m_deleterTypes.end()) { writeUniqueTypes(os, *deleterTypesIt); } else if (dependencyData.name == "PhysicalDevice") { // special handling for class Device, as it's created from PhysicalDevice, but destroys itself writeUniqueTypes(os, std::make_pair>("", { "Device" })); } const std::string memberName = startLowerCase(dependencyData.name); const std::string templateString = R"( class ${className} { public: VULKAN_HPP_CONSTEXPR ${className}() : m_${memberName}(VK_NULL_HANDLE) {} VULKAN_HPP_CONSTEXPR ${className}( std::nullptr_t ) : m_${memberName}(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT ${className}( Vk${className} ${memberName} ) : m_${memberName}( ${memberName} ) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) ${className} & operator=(Vk${className} ${memberName}) { m_${memberName} = ${memberName}; return *this; } #endif ${className} & operator=( std::nullptr_t ) { m_${memberName} = VK_NULL_HANDLE; return *this; } bool operator==( ${className} const & rhs ) const { return m_${memberName} == rhs.m_${memberName}; } bool operator!=(${className} const & rhs ) const { return m_${memberName} != rhs.m_${memberName}; } bool operator<(${className} const & rhs ) const { return m_${memberName} < rhs.m_${memberName}; } ${commands} VULKAN_HPP_TYPESAFE_EXPLICIT operator Vk${className}() const { return m_${memberName}; } explicit operator bool() const { return m_${memberName} != VK_NULL_HANDLE; } bool operator!() const { return m_${memberName} == VK_NULL_HANDLE; } private: Vk${className} m_${memberName}; }; static_assert( sizeof( ${className} ) == sizeof( Vk${className} ), "handle and wrapper have different size!" ); )"; std::ostringstream commands; // now list all the commands that are mapped to members of this class for (size_t i = 0; i < handleData.commands.size(); i++) { std::map::const_iterator cit = m_commands.find(handleData.commands[i]); assert((cit != m_commands.end()) && !cit->second.className.empty()); writeTypeCommand(commands, " ", cit->second, false); // special handling for destroy functions which are not aliased. if (!cit->second.isAlias && (((cit->second.fullName.substr(0, 7) == "destroy") && (cit->second.reducedName != "destroy")) || (cit->second.fullName.substr(0, 4) == "free"))) { CommandData shortenedCommand = cit->second; shortenedCommand.reducedName = (cit->second.fullName.substr(0, 7) == "destroy") ? "destroy" : "free"; writeTypeCommand(commands, " ", shortenedCommand, false); } } os << replaceWithMap(templateString, { { "className", dependencyData.name }, { "memberName", memberName }, { "commands", commands.str() } }); // and finally the commands, that are member functions of this handle for (size_t i = 0; i < handleData.commands.size(); i++) { std::string commandName = handleData.commands[i]; std::map::const_iterator cit = m_commands.find(commandName); assert((cit != m_commands.end()) && !cit->second.className.empty()); std::list::const_iterator dep = std::find_if(m_dependencies.begin(), m_dependencies.end(), [commandName](DependencyData const& dd) { return dd.name == commandName; }); assert(dep != m_dependencies.end() && (dep->name == cit->second.fullName)); writeTypeCommand(os, " ", cit->second, true); // special handling for destroy functions if (!cit->second.isAlias && (((cit->second.fullName.substr(0, 7) == "destroy") && (cit->second.reducedName != "destroy")) || (cit->second.fullName.substr(0, 4) == "free"))) { CommandData shortenedCommand = cit->second; shortenedCommand.reducedName = (cit->second.fullName.substr(0, 7) == "destroy") ? "destroy" : "free"; writeTypeCommand(os, " ", shortenedCommand, true); } } if (!handleData.alias.empty()) { os << " using " << handleData.alias << " = " << dependencyData.name << ";" << std::endl << std::endl; } leaveProtect(os, handleData.protect); } void VulkanHppGenerator::writeTypes(std::ostream & os, std::map const& defaultValues) { assert(m_deleterTypes.find("") != m_deleterTypes.end()); for (std::list::const_iterator it = m_dependencies.begin(); it != m_dependencies.end(); ++it) { switch (it->category) { case DependencyData::Category::BITMASK: assert(m_bitmasks.find(it->name) != m_bitmasks.end()); writeTypeBitmask(os, it->name, m_bitmasks.find(it->name)->second, m_enums.find(generateEnumNameForFlags(it->name))->second); break; case DependencyData::Category::COMMAND: writeTypeCommand(os, *it); break; case DependencyData::Category::ENUM: assert(m_enums.find(it->name) != m_enums.end()); writeTypeEnum(os, m_enums.find(it->name)->second); break; case DependencyData::Category::FUNC_POINTER: case DependencyData::Category::REQUIRED: // skip FUNC_POINTER and REQUIRED, they just needed to be in the dependencies list to resolve dependencies break; case DependencyData::Category::HANDLE: assert(m_handles.find(it->name) != m_handles.end()); writeTypeHandle(os, *it, m_handles.find(it->name)->second); break; case DependencyData::Category::SCALAR: writeTypeScalar(os, *it); break; case DependencyData::Category::STRUCT: writeTypeStruct(os, *it, defaultValues); break; case DependencyData::Category::UNION: assert(m_structs.find(it->name) != m_structs.end()); writeTypeUnion(os, *it, defaultValues); break; default: assert(false); break; } } } void VulkanHppGenerator::writeTypeScalar(std::ostream & os, DependencyData const& dependencyData) { assert(dependencyData.dependencies.size() == 1); os << " using " << dependencyData.name << " = " << *dependencyData.dependencies.begin() << ";" << std::endl << std::endl; } void VulkanHppGenerator::writeTypeStruct(std::ostream & os, DependencyData const& dependencyData, std::map const& defaultValues) { std::map::const_iterator it = m_structs.find(dependencyData.name); assert(it != m_structs.end()); enterProtect(os, it->second.protect); os << " struct " << dependencyData.name << std::endl << " {" << std::endl; // only structs that are not returnedOnly get a constructor! if (!it->second.returnedOnly) { writeStructConstructor(os, dependencyData.name, it->second, defaultValues); } // create the setters if (!it->second.returnedOnly) { for (size_t i = 0; i < it->second.members.size(); i++) { writeStructSetter(os, dependencyData.name, it->second.members[i]); } } // the implicit cast-operators to the native type os << " operator Vk" << dependencyData.name << " const&() const" << std::endl << " {" << std::endl << " return *reinterpret_cast(this);" << std::endl << " }" << std::endl << std::endl << " operator Vk" << dependencyData.name << " &()" << std::endl << " {" << std::endl << " return *reinterpret_cast(this);" << std::endl << " }" << std::endl << std::endl; // operator==() and operator!=() // only structs without a union as a member can have a meaningfull == and != operation; we filter them out if (!containsUnion(dependencyData.name, m_structs)) { // two structs are compared by comparing each of the elements os << " bool operator==( " << dependencyData.name << " const& rhs ) const" << std::endl << " {" << std::endl << " return "; for (size_t i = 0; i < it->second.members.size(); i++) { if (i != 0) { os << std::endl << " && "; } if (!it->second.members[i].arraySize.empty()) { os << "( memcmp( " << it->second.members[i].name << ", rhs." << it->second.members[i].name << ", " << it->second.members[i].arraySize << " * sizeof( " << it->second.members[i].type << " ) ) == 0 )"; } else { os << "( " << it->second.members[i].name << " == rhs." << it->second.members[i].name << " )"; } } os << ";" << std::endl << " }" << std::endl << std::endl << " bool operator!=( " << dependencyData.name << " const& rhs ) const" << std::endl << " {" << std::endl << " return !operator==( rhs );" << std::endl << " }" << std::endl << std::endl; } // the member variables for (size_t i = 0; i < it->second.members.size(); i++) { if (it->second.members[i].type == "StructureType") { assert((i == 0) && (it->second.members[i].name == "sType")); if (!it->second.members[i].values.empty()) { assert(!it->second.members[i].values.empty()); auto nameIt = m_nameMap.find(it->second.members[i].values); assert(nameIt != m_nameMap.end()); os << " private:" << std::endl << " StructureType sType = " << nameIt->second << ";" << std::endl << std::endl << " public:" << std::endl; } else { os << " StructureType sType;" << std::endl; } } else { os << " " << it->second.members[i].type << " " << it->second.members[i].name; if (it->second.members[i].name == "pNext") { os << " = nullptr"; } else if (!it->second.members[i].arraySize.empty()) { os << "[" << it->second.members[i].arraySize << "]"; } os << ";" << std::endl; } } os << " };" << std::endl << " static_assert( sizeof( " << dependencyData.name << " ) == sizeof( Vk" << dependencyData.name << " ), \"struct and wrapper have different size!\" );" << std::endl; if (!it->second.alias.empty()) { os << std::endl << " using " << it->second.alias << " = " << dependencyData.name << ";" << std::endl; } leaveProtect(os, it->second.protect); os << std::endl; } void VulkanHppGenerator::writeUniqueTypes(std::ostream &os, std::pair> const& deleterTypes) { os << "#ifndef VULKAN_HPP_NO_SMART_HANDLE" << std::endl; if (!deleterTypes.first.empty()) { os << " class " << deleterTypes.first << ";" << std::endl; } os << std::endl; for (auto const& dt : deleterTypes.second) { auto ddit = m_deleters.find(dt); assert(ddit != m_deleters.end()); os << " template class UniqueHandleTraits<" << dt << ",Dispatch> {public: using deleter = " << (ddit->second.pool.empty() ? "Object" : "Pool") << ((ddit->second.call.substr(0, 4) == "free") ? "Free<" : "Destroy<") << (deleterTypes.first.empty() ? "NoParent" : deleterTypes.first) << (ddit->second.pool.empty() ? "" : ", " + ddit->second.pool) << ",Dispatch>; };\n"; os << " using Unique" << dt << " = UniqueHandle<" << dt << ",DispatchLoaderStatic>;" << std::endl; } os << "#endif /*VULKAN_HPP_NO_SMART_HANDLE*/" << std::endl << std::endl; } void VulkanHppGenerator::writeTypeUnion(std::ostream & os, DependencyData const& dependencyData, std::map const& defaultValues) { std::map::const_iterator it = m_structs.find(dependencyData.name); assert(it != m_structs.end()); std::ostringstream oss; os << " union " << dependencyData.name << std::endl << " {" << std::endl; for (size_t i = 0; isecond.members.size(); i++) { // one constructor per union element os << " " << dependencyData.name << "( "; if (it->second.members[i].arraySize.empty()) { os << it->second.members[i].type << " "; } else { os << "const std::array<" << it->second.members[i].type << "," << it->second.members[i].arraySize << ">& "; } os << it->second.members[i].name << "_"; // just the very first constructor gets default arguments if (i == 0) { std::map::const_iterator defaultIt = defaultValues.find(it->second.members[i].pureType); assert(defaultIt != defaultValues.end()); if (it->second.members[i].arraySize.empty()) { os << " = " << defaultIt->second; } else { os << " = { {" << defaultIt->second << "} }"; } } os << " )" << std::endl << " {" << std::endl << " "; if (it->second.members[i].arraySize.empty()) { os << it->second.members[i].name << " = " << it->second.members[i].name << "_"; } else { os << "memcpy( &" << it->second.members[i].name << ", " << it->second.members[i].name << "_.data(), " << it->second.members[i].arraySize << " * sizeof( " << it->second.members[i].type << " ) )"; } os << ";" << std::endl << " }" << std::endl << std::endl; } for (size_t i = 0; isecond.members.size(); i++) { // one setter per union element assert(!it->second.returnedOnly); writeStructSetter(os, dependencyData.name, it->second.members[i]); } // the implicit cast operators to the native type os << " operator Vk" << dependencyData.name << " const&() const" << std::endl << " {" << std::endl << " return *reinterpret_cast(this);" << std::endl << " }" << std::endl << std::endl << " operator Vk" << dependencyData.name << " &()" << std::endl << " {" << std::endl << " return *reinterpret_cast(this);" << std::endl << " }" << std::endl << std::endl; // the union member variables // if there's at least one Vk... type in this union, check for unrestricted unions support bool needsUnrestrictedUnions = false; for (size_t i = 0; i < it->second.members.size() && !needsUnrestrictedUnions; i++) { needsUnrestrictedUnions = (m_vkTypes.find(it->second.members[i].type) != m_vkTypes.end()); } if (needsUnrestrictedUnions) { os << "#ifdef VULKAN_HPP_HAS_UNRESTRICTED_UNIONS" << std::endl; for (size_t i = 0; i < it->second.members.size(); i++) { os << " " << it->second.members[i].type << " " << it->second.members[i].name; if (!it->second.members[i].arraySize.empty()) { os << "[" << it->second.members[i].arraySize << "]"; } os << ";" << std::endl; } os << "#else" << std::endl; } for (size_t i = 0; i < it->second.members.size(); i++) { os << " "; if (m_vkTypes.find(it->second.members[i].type) != m_vkTypes.end()) { os << "Vk"; } os << it->second.members[i].type << " " << it->second.members[i].name; if (!it->second.members[i].arraySize.empty()) { os << "[" << it->second.members[i].arraySize << "]"; } os << ";" << std::endl; } if (needsUnrestrictedUnions) { os << "#endif // VULKAN_HPP_HAS_UNRESTRICTED_UNIONS" << std::endl; } os << " };" << std::endl << std::endl; } #if !defined(NDEBUG) void VulkanHppGenerator::checkExtensionRequirements() { for (auto const& ext : m_extensions) { for (auto const& req : ext.second.requires) { auto reqExt = m_extensions.find(req); assert(reqExt != m_extensions.end()); assert(reqExt->second.protect.empty() || (reqExt->second.protect == ext.second.protect)); } } } void VulkanHppGenerator::skipVendorID(tinyxml2::XMLElement const* element) { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "comment",{} },{ "id",{} },{ "name",{} } }, {}); checkElements(getChildElements(element), {}); VendorIDData vendorID; for (auto const& attribute : attributes) { std::string name = attribute.first; if (name == "comment") { vendorID.comment = attribute.second; } else if (name == "id") { vendorID.id = attribute.second; } else { assert(name == "name"); vendorID.name = attribute.second; } } m_vendorIDs.push_back(vendorID); } void VulkanHppGenerator::skipVendorIDs(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); checkElements(children, { "vendorid" }); for (auto child : children) { skipVendorID(child); } } #endif void VulkanHppGenerator::EnumData::addEnumValue(std::string const &enumName, std::string const& tag, std::map & nameMap) { EnumValueData evd; evd.name = createEnumValueName(enumName, prefix, postfix, bitmask, tag); evd.value = enumName; auto it = std::find_if(values.begin(), values.end(), [&evd](EnumValueData const& _evd) { return _evd.name == evd.name; }); if (it == values.end()) { values.push_back(evd); assert(nameMap.find(enumName) == nameMap.end()); nameMap[enumName] = this->name + "::" + evd.name; } else { assert(it->value == evd.value); } } void VulkanHppGenerator::writeDelegationClassStatic(std::ostream &os) { os << "class DispatchLoaderStatic" << std::endl << "{" << std::endl << "public:\n"; for (auto command : m_commands) { enterProtect(os, command.second.protect); os << " " << command.second.unchangedReturnType << " vk" << startUpperCase(command.second.fullName) << "( "; bool first = true; for (auto param : command.second.params) { if (!first) { os << ", "; } os << param.unchangedType << " " << param.name; if (!param.arraySize.empty()) { os << "[" << param.arraySize << "]"; } first = false; } os << " ) const\n" << " {\n" << " return ::vk" << startUpperCase(command.second.fullName) << "( "; first = true; for (auto param : command.second.params) { if (!first) { os << ", "; } os << param.name; first = false; } os << ");\n"; os << " }\n"; leaveProtect(os, command.second.protect); } os << "};\n"; } void VulkanHppGenerator::writeDelegationClassDynamic(std::ostream &os) { os << " class DispatchLoaderDynamic" << std::endl << " {" << std::endl << " public:" << std::endl; for (auto command : m_commands) { enterProtect(os, command.second.protect); os << " PFN_vk" << startUpperCase(command.second.fullName) << " vk" << startUpperCase(command.second.fullName) << " = 0;" << std::endl; leaveProtect(os, command.second.protect); } // write initialization function to fetch function pointers os << " public:" << std::endl << " DispatchLoaderDynamic(Instance instance = Instance(), Device device = Device())" << std::endl << " {" << std::endl << " if (instance)" << std::endl << " {" << std::endl << " init(instance, device);" << std::endl << " }" << std::endl << " }" << std::endl << std::endl << " void init(Instance instance, Device device = Device())" << std::endl << " {" << std::endl; for (auto command : m_commands) { enterProtect(os, command.second.protect); if (!command.second.params.empty() && m_handles.find(command.second.params[0].type) != m_handles.end() && command.second.params[0].type != "Instance") { os << " vk" << startUpperCase(command.second.fullName) << " = PFN_vk" << startUpperCase(command.second.fullName) << "(device ? device.getProcAddr( \"vk" << startUpperCase(command.second.fullName) << "\") : instance.getProcAddr( \"vk" << startUpperCase(command.second.fullName) << "\"));" << std::endl; } else { os << " vk" << startUpperCase(command.second.fullName) << " = PFN_vk" << startUpperCase(command.second.fullName) << "(instance.getProcAddr( \"vk" << startUpperCase(command.second.fullName) << "\"));" << std::endl; } leaveProtect(os, command.second.protect); } os << " }" << std::endl; os << " };\n"; } int main( int argc, char **argv ) { try { tinyxml2::XMLDocument doc; std::string filename = (argc == 1) ? VK_SPEC : argv[1]; std::cout << "Loading vk.xml from " << filename << std::endl; std::cout << "Writing vulkan.hpp to " << VULKAN_HPP_FILE << std::endl; tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_SUCCESS) { std::cout << "VkGenerate: failed to load file " << filename << " . Error code: " << error << std::endl; return -1; } VulkanHppGenerator generator; bool foundLicense = false; tinyxml2::XMLElement const* registryElement = doc.FirstChildElement(); checkAttributes(getAttributes(registryElement), registryElement->GetLineNum(), {}, {}); assert(strcmp(registryElement->Value(), "registry") == 0); assert(!registryElement->NextSiblingElement()); std::vector children = getChildElements(registryElement); checkElements(children, { "commands", "comment", "enums", "extensions", "feature", "tags", "types", "vendorids", "platforms" }); for (auto child : children) { const std::string value = child->Value(); if (value == "commands") { generator.readCommands(child); } else if (value == "comment") { if (!foundLicense) { // get the vulkan license header and skip any leading spaces generator.readComment(child); foundLicense = true; } } else if (value == "enums") { generator.readEnums(child); } else if (value == "extensions") { generator.readExtensions(child); } else if (value == "feature") { generator.readFeature(child); } else if (value == "tags") { generator.readTags(child); } else if (value == "types") { generator.readTypes(child); } else if (value == "vendorids") { #if !defined(NDEBUG) generator.skipVendorIDs(child); #endif } else if (value == "platforms") { // skip this tag } else { std::stringstream lineNumber; lineNumber << child->GetLineNum(); std::cerr << "warning: Unhandled tag " << value << " at line number: " << lineNumber.str() << "!" << std::endl; } } generator.sortDependencies(); #if !defined(NDEBUG) generator.checkExtensionRequirements(); #endif std::map defaultValues = generator.createDefaults(); std::ofstream ofs(VULKAN_HPP_FILE); ofs << generator.getVulkanLicenseHeader() << std::endl << R"( #ifndef VULKAN_HPP #define VULKAN_HPP #include #include #include #include #include #include #include #include #include #include #include #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE # include # include #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #if !defined(VULKAN_HPP_ASSERT) # include # define VULKAN_HPP_ASSERT assert #endif // includes through some other header // this results in major(x) being resolved to gnu_dev_major(x) // which is an expression in a constructor initializer list. #if defined(major) #undef major #endif #if defined(minor) #undef minor #endif // Windows defines MemoryBarrier which is deprecated and collides // with the vk::MemoryBarrier struct. #ifdef MemoryBarrier #undef MemoryBarrier #endif )"; writeVersionCheck(ofs, generator.getVersion()); writeTypesafeCheck(ofs, generator.getTypesafeCheck()); ofs << versionCheckHeader << inlineHeader << explicitHeader << constExprHeader << std::endl << vkNamespace << flagsHeader << optionalClassHeader << arrayProxyHeader << uniqueHandleHeader << structureChainHeader; // first of all, write out vk::Result and the exception handling stuff generator.writeResultEnum(ofs); ofs << "} // namespace VULKAN_HPP_NAMESPACE" << std::endl << std::endl << "namespace std" << std::endl << "{" << std::endl << " template <>" << std::endl << " struct is_error_code_enum : public true_type" << std::endl << " {};" << std::endl << "}" << std::endl << std::endl << "namespace VULKAN_HPP_NAMESPACE" << std::endl << "{" << std::endl << resultValueHeader << createResultValueHeader; generator.writeDelegationClassStatic(ofs); ofs << deleterClassString; generator.writeTypes(ofs, defaultValues); generator.writeStructureChainValidation(ofs); generator.writeToStringFunctions(ofs); generator.writeDelegationClassDynamic(ofs); ofs << "} // namespace VULKAN_HPP_NAMESPACE" << std::endl << std::endl << "#endif" << std::endl; } 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; } }