L'objectif du Vulkan-HPP est de fournir des liaisons C ++ en tête uniquement pour l'API Vulkan C pour améliorer l'expérience des développeurs Vulkan sans introduire le coût d'exécution du processeur. Il ajoute des fonctionnalités telles que la sécurité de type pour les énumérations et les champs de bit, le support de conteneur STL, les exceptions et les énumérations simples.
| Plate-forme | Statut de construction |
|---|---|
| Linux |
Vulkan-HPP fait partie du SDK Lunarg Vulkan depuis la version 1.0.24. Juste #include <vulkan/vulkan.hpp> Et vous êtes prêt à utiliser les liaisons C ++. Si vous utilisez une version Vulkan non encore prise en charge par le SDK Vulkan, vous pouvez trouver la dernière version des en-têtes ici.
Vulkan-HPP nécessite un compilateur capable C ++ 11 à compiler. Les compilateurs suivants sont connus pour fonctionner:
Pour construire les échantillons et tests locaux, vous devrez cloner ce référentiel et exécuter CMake pour générer les fichiers de construction requis
Installez les dépendances.
Ouvrez un shell qui fournit Git et cloner le référentiel avec:
git clone --recurse-submodules https://github.com/KhronosGroup/Vulkan-Hpp.git
Changez le répertoire actuel en répertoire Vulkan-HPP nouvellement créé.
Créez un environnement de construction avec Cmake:
cmake -DVULKAN_HPP_SAMPLES_BUILD=ON -DVULKAN_HPP_SAMPLES_BUILD_WITH_LOCAL_VULKAN_HPP=ON -DVULKAN_HPP_TESTS_BUILD=ON -DVULKAN_HPP_TESTS_BUILD_WITH_LOCAL_VULKAN_HPP=ON -B build
Vous devrez peut-être spécifier un générateur via -G , pour une liste complète des générateurs exécuter cmake -G .
vulkan.hpp à partir du fichier de registre XML vk.xml , ajoutez l' -DVULKAN_HPP_RUN_GENERATOR=ON l'option à la ligne de commande cmake. Ouvrez le projet généré avec un IDE, par exemple Visual Studio, soit lancez le processus de construction avec cmake --build build --parallel .
Facultatif: pour mettre à jour le Vulkan-HPP et ses sous-modules exécutent git pull --recurse-submodules .
Vous pouvez télécharger et installer Vulkan-HPP à l'aide du gestionnaire de dépendance VCPKG:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install vulkan-headersLe port Vulkan-HPP de VCPKG est tenu à jour par les membres de l'équipe Microsoft et les contributeurs communautaires. Si la version est obsolète, veuillez créer une demande de problème ou d'extraction sur le référentiel VCPKG.
Si le format Clang-Format du programme est trouvé par CMake, le Define CLANG_FORMAT_EXECUTABLE est défini en conséquence. Dans ce cas, le vulkan.hpp généré est formaté à l'aide du fichier .clang-format situé dans le répertoire racine de ce projet. Sinon, il est formaté comme codé dur dans le générateur.
Le fichier VulkanHpp.natvis fournit une vue personnalisée sur vk::Flags pour Visual Studio. Si vous ajoutez ce fichier au répertoire NATVIS spécifique à l'utilisateur de votre installation Visual Studio (% UserProfile% Documents Visual Studio 2022 Visualizers), vous obtenez vk::Flags bien formaté dans votre débogueur avec tous vos projets Visual Studio.
Pour éviter les collisions de noms avec l'API Vulkan C, les liaisons C ++ résident dans l'espace de noms vk . Les règles suivantes s'appliquent à la nouvelle dénomination:
Vk . En plus de cela, la première lettre de fonctions est en minuscules.vkCreateInstance est accessible en tant que vk::createInstance .VkImageTiling est accessible en tant que vk::ImageTiling .VkImageCreateInfo est accessible en tant que vk::ImageCreateInfo .VK_ et Type Infix supprimé. Si le type d'énumération est une extension, le suffixe d'extension a été supprimé des valeurs d'énumération.Dans tous les autres cas, le suffixe d'extension n'a pas été supprimé.
VK_IMAGETYPE_2D est maintenant vk::ImageType::e2D .VK_COLOR_SPACE_SRGB_NONLINEAR_KHR EST vk::ColorSpaceKHR::eSrgbNonlinear .VK_STRUCTURE_TYPE_PRESENT_INFO_KHR est maintenant vk::StructureType::ePresentInfoKHR ._BIT a également été supprimé. Dans certains cas, il peut être nécessaire de déplacer le Vulkan-HPP vers un espace de noms personnalisé. Cela peut être réalisé en définissant VULKAN_HPP_NAMESPACE avant d'inclure Vulkan-HPP.
Vulkan-HPP déclare une classe pour toutes les poignées afin d'assurer la sécurité complète et d'ajouter la prise en charge des fonctions des membres sur les poignées. Une fonction membre a été ajoutée à une classe de manche pour chaque fonction qui accepte la poignée correspondante comme premier paramètre. Au lieu de vkBindBufferMemory(device, ...) on peut écrire device.bindBufferMemory(...) ou vk::bindBufferMemory(device, ...) .
Il y a un en-tête supplémentaire nommé vulkan_raii.hpp généré. Cet en-tête maintient des classes de wrapper conformes à Raii pour les types de poignée. Autrement dit, pour par exemple le type de poignée VkInstance , il y a un wrapper compatible vk::raii::Instance . Veuillez consulter les échantillons en utilisant ces classes dans le répertoire RAII_SEMPLES.
Sur les plates-formes 64 bits, Vulkan-HPP prend en charge les conversions implicites entre les poignées C ++ Vulkan et les poignées V Vulkan. Sur les plates-formes 32 bits, toutes les poignées non dissalables sont définies comme uint64_t , empêchant ainsi les vérifications de conversion de type au moment de la compilation qui prendrait les affectations entre les types de manche incompatibles. En raison de ce que Vulkan-HPP n'activait pas la conversion implicite pour les plates-formes 32 bits par défaut et il est recommandé d'utiliser un static_cast pour la conversion comme celle-ci: VkImage = static_cast<VkImage>(cppImage) pour empêcher la conversion de certains intres arbitraires en une poignée ou vice versa par accident. Si vous développez votre code sur une plate-forme 64 bits, mais que vous souhaitez compiler votre code pour une plate-forme 32 bits sans ajouter les moulages explicites, vous pouvez définir VULKAN_HPP_TYPESAFE_CONVERSION à 1 dans votre système de construction ou avant d'inclure vulkan.hpp . Sur les plates-formes 64 bits, cette définition est définie sur 1 par défaut et peut être définie sur 0 pour désactiver les conversions implicites.
La fonction Enum SCOPED ajoute la sécurité & type aux drapeaux, mais empêche également d'utiliser les bits de signalement comme entrée pour les opérations | .
Comme la solution Vulkan-HPP fournit un modèle de classe vk::Flags qui apporte les opérations standard comme &= , |= , & , et | à nos énumérations portée. À l'exception de l'initialisation avec 0 cette classe se comporte exactement comme un massasque normal avec l'amélioration qu'il est impossible de définir des bits non spécifiés par l'énumération correspondante par accident. Voici quelques exemples pour la manipulation de Bitmask:
vk::ImageUsageFlags iu1; // initialize a bitmask with no bit set
vk::ImageUsageFlags iu2 = {}; // initialize a bitmask with no bit set
vk::ImageUsageFlags iu3 = vk::ImageUsageFlagBits::eColorAttachment; // initialize with a single value
vk::ImageUsageFlags iu4 = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage; // or two bits to get a bitmask
PipelineShaderStageCreateInfo ci ({} /* pass a flag without any bits set */ , ...); Lors de la construction d'une poignée dans Vulkan, il faut généralement créer une structure CreateInfo qui décrit la nouvelle poignée. Cela peut entraîner un code assez long comme on peut le voir dans l'exemple de Vulkan C suivant:
VkImageCreateInfo ci;
ci. sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
ci.pNext = nullptr ;
ci.flags = ...some flags...;
ci.imageType = VK_IMAGE_TYPE_2D;
ci.format = VK_FORMAT_R8G8B8A8_UNORM;
ci.extent = VkExtent3D { width, height, 1 };
ci.mipLevels = 1 ;
ci.arrayLayers = 1 ;
ci.samples = VK_SAMPLE_COUNT_1_BIT;
ci.tiling = VK_IMAGE_TILING_OPTIMAL;
ci.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
ci.queueFamilyIndexCount = 0 ;
ci.pQueueFamilyIndices = 0 ;
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
vkCreateImage (device, &ci, allocator, &image); Il y a deux problèmes typiques que les développeurs Vulkan rencontrent lors du remplissage d'un champ de structure CreateInfo par champ:
sType est incorrect.Surtout le premier est difficile à détecter.
Vulkan-HPP fournit des constructeurs pour tous les objets CreateInfo qui acceptent un paramètre pour chaque variable de membre. De cette façon, le compilateur lance une erreur du compilateur si une valeur a été oubliée. En plus de ce sType , est automatiquement rempli de la valeur correcte et de la définition pNext sur un nullptr par défaut. Voici à quoi ressemble le même code avec un constructeur:
vk::ImageCreateInfo ci ({}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm,
{ width, height, 1 },
1 , 1 , vk::SampleCountFlagBits:: e1 ,
vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment,
vk::SharingMode::eExclusive, 0 , nullptr , vk::ImageLayout::eUndefined);
vk::Image image = device.createImage(ci); Avec des constructeurs pour CreateInfo Structures, on peut également passer des temporaires à des fonctions Vulkan comme celle-ci:
vk::Image image = device.createImage({{}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm,
{ width, height, 1 },
1 , 1 , vk::SampleCountFlagBits:: e1 ,
vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment,
vk::SharingMode::eExclusive, 0 , nullptr , vk::ImageLayout::eUndefined}); En commençant par C ++ 20, C ++ prend en charge les initialiseurs désignés. Comme cette fonctionnalité nécessite de ne pas avoir de constructeurs diclatés ou héréditaires, vous devez #define VULKAN_HPP_NO_CONSTRUCTORS , qui supprime toutes les structures et les constructeurs syndicaux de vulkan.hpp . Au lieu de cela, vous pouvez ensuite utiliser l'initialisation agrégée. Les premiers VK-lines de votre source peuvent ensuite ressembler:
// initialize the vk::ApplicationInfo structure
vk::ApplicationInfo applicationInfo{ . pApplicationName = AppName,
. applicationVersion = 1 ,
. pEngineName = EngineName,
. engineVersion = 1 ,
. apiVersion = VK_API_VERSION_1_1 };
// initialize the vk::InstanceCreateInfo
vk::InstanceCreateInfo instanceCreateInfo{ . pApplicationInfo = &applicationInfo };au lieu de
// initialize the vk::ApplicationInfo structure
vk::ApplicationInfo applicationInfo (AppName, 1 , EngineName, 1 , VK_API_VERSION_1_1);
// initialize the vk::InstanceCreateInfo
vk::InstanceCreateInfo instanceCreateInfo ({}, &applicationInfo); Notez que l'ordre des concepteurs doit correspondre à l'ordre de déclaration. Remarque également, que vous pouvez maintenant définir explicitement le membre sType de VK-structures. Ce n'est ni nécessaire (car ils sont correctement initialisés par défaut) ni recommandés.
L'API Vulkan a plusieurs endroits qui nécessitent (compter, pointeur) comme deux arguments de fonction et C ++ a quelques conteneurs qui mappent parfaitement à cette paire. Pour simplifier le développement, les liaisons Vulkan-HPP ont remplacé ces paires d'argument par le modèle de classe vk::ArrayProxy qui accepte les tableaux vides et une seule valeur ainsi que les conteneurs STL std::initializer_list , std::array et std::vector comme argument pour la construction. De cette façon, une version Vulkan générée peut accepter une variété d'entrées sans avoir l'explosion combinatoire qui se produirait lors de la création d'une fonction pour chaque type de conteneur.
Voici quelques échantillons de code sur la façon d'utiliser le vk::ArrayProxy :
vk::CommandBuffer c;
// pass an empty array
c.setScissor( 0 , nullptr );
// pass a single value. Value is passed as reference
vk::Rect2D scissorRect = { { 0 , 0 }, { 640 , 480 } };
c.setScissor( 0 , scissorRect);
// pass a temporary value.
c.setScissor( 0 , { { 0 , 0 }, { 640 , 480 } });
// pass a fixed size array
vk::Rect2D scissorRects[ 2 ] = { { { 0 , 0 }, { 320 , 240 } }, { { 320 , 240 }, { 320 , 240 } } };
c.setScissor( 0 , scissorRects);
// generate a std::initializer_list using two rectangles from the stack. This might generate a copy of the rectangles.
vk::Rect2D scissorRect1 = { { 0 , 0 }, { 320 , 240 } };
vk::Rect2D scissorRect2 = { { 320 , 240 }, { 320 , 240 } };
c.setScissor( 0 , { scissorRect, scissorRect2 });
// construct a std::initializer_list using two temporary rectangles.
c.setScissor( 0 , { { { 0 , 0 }, { 320 , 240 } },
{ { 320 , 240 }, { 320 , 240 } } });
// pass a std::array
std::array<vk::Rect2D, 2 > arr{ scissorRect1, scissorRect2 };
c.setScissor( 0 , arr);
// pass a std::vector of dynamic size
std::vector<vk::Rect2D> vec;
vec.push_back(scissorRect1);
vec.push_back(scissorRect2);
c.setScissor( 0 , vec); Vulkan-HPP génère des références pour les pointeurs vers les structures. Cette conversion permet de passer des structures temporaires à des fonctions qui peuvent entraîner un code plus court. Dans le cas où l'entrée est facultative et acceptant ainsi un pointeur nul, le type de paramètre sera vk::Optional<T> const& . Ce type accepte soit une référence à T ou nullptr comme entrée et permet donc de structures temporaires facultatives.
// C
VkImageSubresource subResource;
subResource.aspectMask = 0 ;
subResource.mipLevel = 0 ;
subResource.arrayLayer = 0 ;
VkSubresourceLayout layout;
vkGetImageSubresourceLayout (device, image, &subresource, &layout);
// C++
auto layout = device.getImageSubresourceLayout(image, { {} /* flags */ , 0 /* miplevel */ , 0 /* arrayLayer */ }); Vulkan permet un chaînage de structures à travers le pointeur pNext . Vulkan-HPP possède un modèle de classe variatique qui permet de construire de telles chaînes de structure avec un minimum d'efforts. En plus de cela, il vérifie le temps de compilation si la spécification permet la construction d'une telle chaîne pNext .
// This will compile successfully.
vk::StructureChain<vk::MemoryAllocateInfo, vk::ImportMemoryFdInfoKHR> c;
vk::MemoryAllocateInfo &allocInfo = c.get<vk::MemoryAllocateInfo>();
vk::ImportMemoryFdInfoKHR &fdInfo = c.get<vk::ImportMemoryFdInfoKHR>();
// This will fail compilation since it's not valid according to the spec.
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedRequirementsKHR> c;
vk::MemoryAllocateInfo &allocInfo = c.get<vk::MemoryAllocateInfo>();
vk::ImportMemoryFdInfoKHR &fdInfo = c.get<vk::ImportMemoryFdInfoKHR>(); Vulkan-HPP fournit un constructeur pour ces chaînes similaires aux objets CreateInfo qui accepte une liste de toutes les structures partielle de la chaîne. Le champ pNext est automatiquement défini sur la valeur correcte:
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> c = {
vk::MemoryAllocateInfo (size, type),
vk::MemoryDedicatedAllocateInfo (image)
}; Si l'une des structures d'une structureChain doit être supprimée, peut-être en raison de certains paramètres facultatifs, vous pouvez utiliser la fonction vk::StructureChain::unlink<ClassType>() . Il modifie le StructureChain de telle sorte que la structure spécifiée ne fait plus partie de la chaîne PNEXT. Remarque que la disposition de la mémoire réelle de la structureChain n'est pas modifiée par cette fonction. Dans le cas où la même structure doit être redevenue à la structureChain, utilisez vk::StructureChain::relink<ClassType>() .
Parfois, l'utilisateur doit passer une chaîne de structure préalloquée pour interroger les informations. Pour ces cas, il existe deux fonctions Getter correspondantes. Un avec un modèle variatique générant une chaîne de structure d'au moins deux éléments pour construire la valeur de retour:
// Query vk::MemoryRequirements2HR and vk::MemoryDedicatedRequirementsKHR when calling Device::getBufferMemoryRequirements2KHR:
auto result = device.getBufferMemoryRequirements2KHR<vk::MemoryRequirements2KHR, vk::MemoryDedicatedRequirementsKHR>({});
vk::MemoryRequirements2KHR &memReqs = result.get<vk::MemoryRequirements2KHR>();
vk::MemoryDedicatedRequirementsKHR &dedMemReqs = result.get<vk::MemoryDedicatedRequirementsKHR>();Pour obtenir uniquement la structure de base, sans chaînage, l'autre fonction de Getter fournie n'a pas besoin d'un argument de modèle pour que la structure soit:
// Query just vk::MemoryRequirements2KHR
vk::MemoryRequirements2KHR memoryRequirements = device.getBufferMemoryRequirements2KHR({}); Par défaut, Vulkan-HPP a des exceptions activées. Cela signifie que Vulkan-HPP vérifie le code de retour de chaque appel de fonction qui renvoie un vk::Result . Si vk::Result est un échec, un std::runtime_error sera lancé. Puisqu'il n'est plus nécessaire de renvoyer le code d'erreur, les liaisons C ++ peuvent désormais renvoyer la valeur de retour souhaitée réelle, c'est-à-dire une poignée Vulkan. Dans ces cas vk::ResultValue<SomeType>::type est défini comme le type retourné.
Pour créer un appareil, vous pouvez maintenant simplement écrire:
vk::Device device = physicalDevice.createDevice(createInfo); Certaines fonctions permettent plus que vk::Result::eSuccess pour être considérée comme un code de réussite. Pour ces fonctions, nous renvoyons toujours un vk::ResultValue<SomeType> . Un exemple est acquireNextImage2KHR , qui peut être utilisé comme ceci:
vk::ResultValue< uint32_t > result = device-> acquireNextImage2KHR (acquireNextImageInfo);
switch (result.result)
{
case vk::Result::eSuccess:
currentBuffer = result. value ;
break ;
case vk::Result::eTimeout:
case vk::Result::eNotReady:
case vk::Result::eSuboptimalKHR:
// do something meaningful
break ;
default :
// should not happen, as other return codes are considered to be an error and throw an exception
break ;
} Au fil du temps, certaines fonctions Vulkan peuvent changer, de sorte qu'elles commencent à prendre en charge plus de codes de résultats que vk::Result::eSuccess comme code de réussite. Ce changement logique ne serait pas visible dans l'API C, mais dans l'API C ++, car une telle fonction renvoie désormais un vk::ResultValue<SomeType> au lieu de SomeType . Dans de tels cas (rares), vous devrez ajuster vos sources CPP pour refléter ce changement d'API.
Si la manipulation des exceptions est désactivée en définissant VULKAN_HPP_NO_EXCEPTIONS le type de vk::ResultValue<SomeType>::type est un struct tenant un vk::Result et un SomeType . Cette structure prend en charge le déballage des valeurs de retour en utilisant std::tie .
Dans le cas où vous ne souhaitez pas utiliser la transformation vk::ArrayProxy et Return Value, vous pouvez toujours appeler la fonction de style C ordinaire. Vous trouverez ci-dessous trois exemples montrant les 3 façons d'utiliser l'API:
Le premier extrait montre comment utiliser l'API sans exceptions et la transformation de la valeur de retour:
// No exceptions, no return value transformation
vk::ShaderModuleCreateInfo createInfo (...);
vk::ShaderModule shader1;
vk::Result result = device.createShaderModule(&createInfo, allocator, &shader1);
if (result.result != vk::Result::eSuccess)
{
handle error code;
cleanup?
return ?
}
vk::ShaderModule shader2;
vk::Result result = device.createShaderModule(&createInfo, allocator, &shader2);
if (result != vk::Result::eSuccess)
{
handle error code;
cleanup?
return ?
}Le deuxième extrait montre comment utiliser l'API en utilisant la transformation de la valeur de retour, mais sans exception. C'est déjà un peu plus court que le code d'origine:
vk::ResultValue<ShaderModule> shaderResult1 = device.createShaderModule({...} /* createInfo temporary */ );
if (shaderResult1.result != vk::Result::eSuccess)
{
handle error code;
cleanup?
return ?
}
// std::tie support.
vk::Result result;
vk::ShaderModule shaderModule2;
std::tie (result, shaderModule2) = device.createShaderModule({...} /* createInfo temporary */ );
if (result != vk::Result::eSuccess)
{
handle error code;
cleanup?
return ?
}Une plus belle façon de déballer le résultat consiste à utiliser des liaisons structurées dans C ++ 17. Ils nous permettront d'obtenir le résultat avec une seule ligne de code:
auto [result, shaderModule2] = device.createShaderModule({...} /* createInfo temporary */ );Enfin, le dernier exemple de code consiste à utiliser des exceptions et une transformation de valeur de retour. Il s'agit du mode par défaut de l'API.
vk::ShaderModule shader1;
vk::ShaderModule shader2;
try
{
shader1 = device. createShaderModule ({...});
shader2 = device. createShaderModule ({...});
}
catch (std:: exception const &e)
{
// handle error and free resources
}Important
Les gardiens Vulkan dans le vk -Namespace ne prennent pas en charge RAII, donc vous devez nettoyer vos ressources dans le gestionnaire d'erreurs! Au lieu de cela, vous pouvez utiliser les classes de wrapper de poignée dans le vk::raii -Namespace.
Avec C ++ 17 et plus, certaines fonctions sont attribuées avec [[nodiscard]] , ce qui entraîne un avertissement si vous n'utilisez en aucune façon la valeur de retour. Vous pouvez désactiver ces avertissements en définissant VULKAN_HPP_NO_NODISCARD_WARNINGS .
Pour la transformation de la valeur de retour, il existe une classe spéciale de valeurs de retour qui nécessitent une manipulation spéciale: les énumérations. Pour les énumérations, vous devez généralement écrire du code comme ceci:
std::vector<LayerProperties, Allocator> properties;
uint32_t propertyCount;
vk::Result result;
do
{
// determine number of elements to query
result = static_cast <vk::Result>( vk::enumerateDeviceLayerProperties (m_physicalDevice, &propertyCount, nullptr ));
if ((result == vk::Result::eSuccess) && propertyCount)
{
// allocate memory & query again
properties. resize (propertyCount);
result = static_cast <vk::Result>( vk::enumerateDeviceLayerProperties (m_physicalDevice, &propertyCount, reinterpret_cast
<VkLayerProperties*>(properties. data ())));
}
} while (result == vk::Result::eIncomplete);
// it's possible that the count has changed, start again if properties was not big enough
properties.resize(propertyCount);Étant donné que l'écriture de cette boucle encore et encore est fastidieuse et sujette à des erreurs, la liaison C ++ s'occupe de l'énumération afin que vous puissiez simplement écrire:
std::vector<LayerProperties> properties = physicalDevice.enumerateDeviceLayerProperties(); Vulkan-HPP fournit une interface vk::UniqueHandle<Type, Deleter> . Pour chaque poignée Vulkan Type vk::Type Il existe une poignée unique vk::UniqueType qui supprimera la ressource Vulkan sous-jacente lors de la destruction, par exemple vk::UniqueBuffer est la poignée unique pour vk::Buffer .
Pour chaque fonction qui construit une poignée Vulkan de type vk::Type Vulkan-HPP fournit une deuxième version qui renvoie un vk::UniqueType . Par exemple pour vk::Device::createBuffer Il y a vk::Device::createBufferUnique et pour vk::allocateCommandBuffers Il y a vk::allocateCommandBuffersUnique .
Notez que l'utilisation vk::UniqueHandle a un coût, car la plupart des suppresseurs doivent stocker le vk::AllocationCallbacks et la poignée parent utilisée pour la construction car elles sont nécessaires pour la destruction automatique.
Vulkan-HPP fournit une interface vk::SharedHandle<Type> . Pour chaque poignée Vulkan Type vk::Type Il y a une poignée partagée vk::SharedType qui supprimera la ressource Vulkan sous-jacente lors de la destruction, par exemple vk::SharedBuffer est la poignée partagée du vk::Buffer .
Contrairement à vk::UniqueHandle , vk::SharedHandle prend la propriété partagée de la ressource ainsi que de son parent. Cela signifie que la poignée des parents ne sera pas détruite tant que toutes les ressources enfants ne seront pas supprimées. Ceci est utile pour les ressources partagées entre plusieurs threads ou objets.
Ce mécanisme garantit une commande de destruction correcte même si le parent vk::SharedHandle est détruit avant son enfant. Sinon, la poignée se comporte comme std::shared_ptr . vk::SharedInstance ou l'un de son objet enfant devrait être le dernier à supprimer (d'abord créé, d'abord dans la déclaration de classe).
Il n'y a pas encore de fonction qui renvoie un vk::SharedHandle directement. Au lieu de cela, vous pouvez construire un vk::SharedHandle à partir d'une vk::Handle :::
vk::Buffer buffer = device.createBuffer(...);
vk::SharedBuffer sharedBuffer (buffer, device); // sharedBuffer now owns the buffer Il existe plusieurs spécialisations de vk::SharedHandle pour différents types de poignées. Par exemple, vk::SharedImage peut prendre un argument supplémentaire pour spécifier si l'image appartient à Swapchain:
vk::Image image = swapchain.getImages(...)[ 0 ]; // get the first image from the swapchain
vk::SharedImage sharedImage (image, device, SwapChainOwns::yes); // sharedImage now owns the image, but won't destroy it Il existe également une spécialisation pour vk::SwapchainKHR qui prend un argument supplémentaire pour spécifier une surface:
vk::SwapchainKHR swapchain = device.createSwapchainKHR(...);
vk::SharedSwapchainKHR sharedSwapchain (swapchain, device, surface); // sharedSwapchain now owns the swapchain and surface Vous pouvez créer une surcharge vk::SharedHandle pour votre propre type de poignée ou vos propres poignées partagées en fournissant plusieurs arguments de modèle à SharedHandleBase :
Avec cela, fournissez une fonction de destruction statique personnalisée internalDestroy , qui prend une poignée parent et une poignée à détruire. N'oubliez pas d'ajouter une déclaration d'ami pour la classe de base.
// Example of a custom shared device, that takes in an instance as a parent
class shared_handle <VkDevice> : public vk::SharedHandleBase<VkDevice, vk::SharedInstance, shared_handle<VkDevice>>
{
using base = vk::SharedHandleBase<VkDevice, vk::SharedInstance, shared_handle<VkDevice>>;
friend base;
public:
shared_handle () = default ;
explicit shared_handle (VkDevice handle, vk::SharedInstance parent) noexcept
: base(handle, std::move(parent)) {}
const auto & getParent () const noexcept
{
return getHeader ();
}
protected:
static void internalDestroy ( const vk::SharedInstance& /* control */ , VkDevice handle) noexcept
{
kDestroyDevice (handle);
}
};L'API sera étendue pour fournir des fonctions de création à l'avenir.
En plus de vk::UniqueHandles et vk::SharedHandles , il existe un ensemble de classes de wrapper pour tous les types de manche qui suivent le RAII-Paradigm (l'acquisition de ressources est l'initialisation), fournie dans l'espace de noms vk::raii .
Alors qu'un vk::UniqueHandle imite une poignée enveloppée par un std::unique_ptr , et un vk::SharedHandle imite une poignée enveloppée par un std::shared_ptr , y compris les informations parentales, un vk::raii::Handle est juste une classe qui acquière le Vk-handle sous-jacent dans son constructeur et en relance dans son destructor. Ainsi, vous êtes libre de les utiliser comme valeurs ou de les envelopper avec un pointeur intelligent.
Outre les vk::Handles , toutes ces classes de wrapper doivent contenir des données supplémentaires et ne sont donc pas identiques binaires avec les Handles C Vulkan.
Comme le vk::UniqueHandles et le vk::SharedHandles utilisent le même répartiteur que les vk::Handles , elles peuvent être facilement mixtes et assorties. Les vk::raii::Handles utilisent des répartiteurs légèrement différents et ne sont donc pas compatibles avec les autres poignées! Autrement dit, pour les vk-Handles , le vk::UniqueHandles et le vk::SharedHandles , vous devez instancier un répartiteur global comme décrit dans https://github.com/khronosgroup/vulkan-hpp#extensions - Pper-device-fonction-poiners. Pour le vk::raii-Handles , cela n'est pas nécessaire, car ils maintiennent leurs propres répartiteurs. Le grand avantage ici est lorsque vous avez plusieurs appareils: les fonctions appelées via le vk::raii-Handles appellent toujours les fonctions spécifiques à l'appareil.
Parfois, il est nécessaire d'utiliser std::vector avec des allocateurs personnalisés. Vulkan-HPP prend en charge les vecteurs avec des allocateurs personnalisés en entrée pour vk::ArrayProxy et pour les fonctions qui renvoient un vecteur. Pour ce dernier cas, ajoutez votre allocateur personnalisé préféré comme argument de modèle à l'appel de fonction comme ceci:
std::vector<LayerProperties, MyCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties<MyCustomAllocator>(); Vous pouvez également utiliser un allocateur personnalisé avec état en le fournissant comme argument à ces fonctions. Malheureusement, pour rendre les compilateurs heureux, vous devez également définir explicitement l'argument de répartition. Pour y par défaut, un simple {} suffirait:
MyStatefulCustomAllocator allocator;
std::vector<LayerProperties, MyStatefulCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties(allocator, {}); Partout sur vulkan.hpp , il y a quelques appels à une fonction d'affirmation. En définissant VULKAN_HPP_ASSERT , vous pouvez spécifier votre propre fonction d'affirmation personnalisée à la place.
Par défaut, VULKAN_HPP_ASSERT_ON_RESULT sera utilisé pour vérifier les résultats lorsque VULKAN_HPP_NO_EXCEPTIONS est défini. Si vous souhaitez gérer vous-même les erreurs, vous pouvez le désactiver / personnaliser comme VULKAN_HPP_ASSERT .
Il y a quelques assertions statiques pour chaque classe de manche et chaque structure dans le fichier vulkan_static_assertions.hpp . Vous pouvez inclure ce fichier dans au moins un de vos fichiers source. En définissant VULKAN_HPP_STATIC_ASSERT , vous pouvez spécifier votre propre affirmation statique personnalisée à utiliser pour ces cas. Autrement dit, en le définissant comme un NOP, vous pouvez réduire un peu votre temps de compilation.
Le chargeur Vulkan expose uniquement les fonctions de noyau Vulkan et un nombre limité d'extensions. Pour utiliser Vulkan-HPP avec des extensions, il est nécessaire d'avoir une bibliothèque qui fournit des talons à toutes les fonctions Vulkan utilisées ou de dire à Vulkan-HPP pour expédier ces fonctions de fonctions. Vulkan-HPP fournit un mécanisme de répartition par fonction en acceptant une classe de répartition comme dernier paramètre dans chaque appel de fonction. La classe de répartition doit fournir un type appelant pour chaque fonction Vulkan utilisée. Vulkan-HPP fournit une implémentation, DispatchLoaderDynamic , qui récupère tous les pointeurs de fonction connus de la bibliothèque.
// Providing a function pointer resolving vkGetInstanceProcAddr, just the few functions not depending an an instance or a device are fetched
vk::DispatchLoaderDynamic dld (getInstanceProcAddr);
// Providing an already created VkInstance and a function pointer resolving vkGetInstanceProcAddr, all functions are fetched
vk::DispatchLoaderDynamic dldi (instance, getInstanceProcAddr);
// Providing also an already created VkDevice and optionally a function pointer resolving vkGetDeviceProcAddr, all functions are fetched as well, but now device-specific functions are fetched via vkDeviceGetProcAddr.
vk::DispatchLoaderDynamic dldid ( nstance, getInstanceProcAddr, device);
// Pass dispatch class to function call as last parameter
device.getQueue(graphics_queue_family_index, 0 , &graphics_queue, dldid); Pour utiliser le vk::DispatchLoaderDynamic comme répartiteur par défaut (signifie: vous n'avez pas besoin de l'ajouter explicitement à chaque appel de fonction), vous devez #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 , et avoir le macro VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE exactement une fois dans votre code source pour fournir un dispositif par défaut. Ensuite, vous pouvez l'utiliser par la macro VULKAN_HPP_DEFAULT_DISPATCHER , comme indiqué dans les extraits de code ci-dessous. La création d'un vk::DispatchLoaderDynamic complet est un processus de deux à trois étapes, où vous avez trois choix pour la première étape:
vk::DynamicLoader : VULKAN_HPP_DEFAULT_DISPATCHER.init();getProcAddress (Comparez avec vk::DynamicLoader dans vulkan.hpp ): YourDynamicLoader ydl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(ydl);Note
Vous devez garder cet objet de chargeur dynamique en vie jusqu'à la fin du dernier appel à une fonction Vulkan dans votre programme. Par exemple en le rendant statique ou en le stockant à l'échelle mondiale.
PFN_vkGetInstanceProcAddr : PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = your_own_function_pointer_getter();
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);vk::Instance pour obtenir tous les autres pointeurs de fonction: vk::Instance instance = vk::createInstance({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);vk::Device pour obtenir des pointeurs de fonction spécifiques à l'appareil std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
assert (!physicalDevices.empty());
vk::Device device = physicalDevices[ 0 ].createDevice({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(device);Après la deuxième étape ci-dessus, le répartiteur est entièrement fonctionnel. L'ajout de la troisième étape peut potentiellement entraîner un code plus efficace. Mais si vous avez l'intention d'utiliser plusieurs appareils, vous pouvez simplement omettre cette troisième étape et laisser le pilote faire le dispositif de périphérique.
Dans certains cas, le stockage du DispatchloaderDynamic doit être intégré dans une DLL. Pour les cas, vous devez définir VULKAN_HPP_STORAGE_SHARED pour dire à Vulkan-HPP que le stockage réside dans une DLL. Lors de la compilation de la DLL avec le stockage, il est également nécessaire de définir VULKAN_HPP_STORAGE_SHARED_EXPORT pour exporter les symboles requis.
Pour toutes les fonctions, que VULKAN_HPP_DEFAULT_DISPATCHER est la valeur par défaut du dernier argument de cette fonction. Si vous souhaitez fournir explicitement le répartiteur pour chaque appel de fonction (lorsque vous avez plusieurs répartiteurs pour différents appareils, par exemple) et que vous souhaitez vous assurer que vous ne manquez accidentellement aucun appel de fonction, vous pouvez définir VULKAN_HPP_NO_DEFAULT_DISPATCHER avant d'inclure vulkan.hpp pour supprimer cet argument par défaut.
vulkan.hpp fournit quelques traits de type, la métaprogrammation du modèle d'assouplissement:
template <typename EnumType, EnumType value> struct CppType maps IndexType valeurs ( IndexType::eUint16 , IndexType::eUint32 , ...) au type correspondant ( uint16_t , uint32_t , ...) par le Type de type membre; Maps ObjectType Values ( ObjectType::eInstance , ObjectType::eDevice , ...) au type correspondant ( vk::Instance , vk::Device , ...) par le Type de type membre; Maps DebugReportObjectType Valeurs ( DebugReportObjectTypeEXT::eInstance , DebugReportObjectTypeEXT::eDevice , ...) au type correspondant ( vk::Instance , vk::Device , ...) par le Type de type membre;template <typename T> struct IndexTypeValue Maps Scalar Types ( uint16_t , uint32_t , ...) à la valeur IndexType correspondante ( IndexType::eUint16 , IndexType::eUint32 , ...).template <typename T> struct isVulkanHandleType maps un type à true if et seulement si c'est une classe de manche ( vk::Instance , vk::Device , ...) par la value membre statique.HandleClass::CType Mapte une classe de manche ( vk::Instance , vk::Device ...) au type C correspondant ( VkInstance , VkDevice , ...) par le type de membre CType .HandleClass::objectType mappe une classe de manche ( vk::Instance , vk::Device , ...) à la valeur ObjectType correspondante ( ObjectType::eInstance , ObjectType::eDevice , ...) par le membre statique objectType .HandleClass::debugReportObjectType mappe une classe de manche ( vk::Instance , vk::Device , ...) à la valeur de DebugReportObjectTypeEXT ( DebugReportObjectTypeEXT::eInstance , DebugReportObjectTypeEXT::eDevice , ...) par le membre statique debugReportObjectType . Avec l'en-tête supplémentaire vulkan_format_traits.hpp , quelques fonctions de trait sur vk::Format sont fournies. Avec C ++ 14 et supérieur, toutes ces fonctions sont marquées comme constexpr , c'est-à-dire avec des arguments appropriés, ils sont résolus au moment de la compilation.
uin8_t blockSize( vk::Format format ); Obtient la taille du bloc Texel de ce format en octets.uint8_t texelsPerBlock( vk::Format format ); Obtient le nombre de Texels dans un bloc Texel.std::array<uint8_t, 3> blockExtent( vk::Format format ); Obtient l'étendue tridimensionnelle des blocs Texel.char const * compressionScheme( vk::Format format ); Obtient une description textuelle du schéma de compression de ce format, ou un texte vide s'il n'est pas compressé.bool isCompressed( vk::Format format ); Certes, si le format est un format comprimé, autrement faux.uint8_t packed( vk::Format format ); Obtient le nombre de bits dans lequel le format est emballé. Un seul élément d'image dans ce format peut être stocké dans le même espace qu'un type scalaire de cette largeur de bit.uint8_t componentCount( vk::Format format ); Obtient le nombre de composants de ce format.bool componentsAreCompressed( vk::Format format ); Certes, si les composants de ce format sont compressés, autrement faux.uint8_t componentBits( vk::Format format, uint8_t component ); Obtient le nombre de bits dans ce composant, s'il n'est pas comprimé, sinon 0.char const * componentName( vk::Format format, uint8_t component ); Obtient le nom de ce composant en tant que corde C.char const * componentNumericFormat( vk::Format format, uint8_t component ); Obtient le format numérique de ce composant en tant que corde C.uint8_t componentPlaneIndex( vk::Format format, uint8_t component ); Obtient l'indice du plan, ce composant se trouve dans.uint8_t planeCount( vk::Format format ); Obtient le nombre de plans d'image de ce format.vk::Format planeCompatibleFormat( vk::Format format, uint8_t plane ); Obtient un format de plan unique compatible avec ce plan.uint8_t planeHeightDivisor( vk::Format format, uint8_t plane ); Obtient la hauteur relative de cet avion. Une valeur de K signifie que ce plan est 1 / K la hauteur du format global.uint8_t planeWidthDivisor( vk::Format format, uint8_t plane ); Obtient la largeur relative de cet avion. Une valeur de K signifie que ce plan est 1 / K la largeur du format global. Avec l'en-tête supplémentaire vulkan_hash.hpp , vous obtenez des spécialisations de std::hash pour les classes de wrapper de poignée et, avec C ++ 14, pour les emballages de structure. Avec VULKAN_HPP_HASH_COMBINE , vous pouvez définir votre propre algorithme de combinaison de hachage pour les éléments de structure.
Avec l'en-tête supplémentaire vulkan_extension_inspection.hpp , certaines fonctions pour inspecter des extensions sont fournies. Avec C ++ 20 et plus, certaines de ces fonctions sont marquées comme constexpr , c'est-à-dire avec des arguments appropriés, ils sont résolus au moment de la compilation. Chaque extension est identifiée par une chaîne tenant son nom. Notez qu'il existe une définition avec ce nom pour chaque extension. Certaines fonctions pourraient fournir des informations qui dépendent de la version Vulkan. Comme toutes les fonctions ici fonctionnent uniquement sur les chaînes, les versions Vulkan sont codées par une chaîne commençant par "vk_version_", suivie de la version majeure et mineure, séparée par un soulignement, comme ceci: "VK_version_1_0".
std::set<std::string> const & getDeviceExtensions(); Obtient toutes les extensions de périphérique spécifiées pour la plate-forme actuelle. Notez que tous ne pourraient pas être pris en charge par les appareils réels.std::set<std::string> const & getInstanceExtensions(); Obtient toutes les extensions d'instance spécifiées pour la plate-forme actuelle. Notez que tous ne peuvent pas être pris en charge par les instances réelles.std::map<std::string, std::string> const & getDeprecatedExtensions(); Obtient une carte de toutes les extensions obsolètes vers l'extension ou la version Vulkan qui est censée remplacer cette fonctionnalité.std::map<std::string, std::vector<std::vector<std::string>>> const & getExtensionDepends( std::string const & extension ); Certaines extensions dépend d'autres extensions. Que les dépendances peuvent différer pour différentes versions Vulkan, et il pourrait y avoir différents ensembles de dépendances pour la même version Vulkan. Cette fonction obtient un vecteur de vecteurs d'extensions par version Vulkan dont dépend l'extension donnée.std::pair<bool, std::vector<std::vector<std::string>> const &> getExtensionDepends( std::string const & version, std::string const & extension ); Le first membre de la std::pair retournée est vraie, si l'extension donnée est spécifiée pour la version Vulkan donnée, autrement false . Le second membre de la std::pair retournée est un vecteur de vecteurs d'extensions, répertoriant les ensembles distincts d'extensions de l'extension donnée dépend de la version Vulkan donnée.std::map<std::string, std::string> const & getObsoletedExtensions(); Obtient une carte de toutes les extensions obsolètes vers l'extension ou la version Vulkan qui a obsolète cette extension.std::map<std::string, std::string> const & getPromotedExtensions(); Obtient une carte de toutes les extensions qui ont été promues à une autre extension ou à une version Vulkan à cette extension de la version Vulkan.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionDeprecatedBy( std::string const & extension ); Obtient l'extension ou la version Vulkan que l'extension donnée est obsolète.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionObsoletedBy( std::string const & extension ); Obtient l'extension ou la version Vulkan que l'extension donnée est obsolète.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionPromotedTo( std::string const & extension ); Obtient l'extension ou la version Vulkan dans laquelle l'extension donnée est promue.VULKAN_HPP_CONSTEXPR_20 bool isDeprecatedExtension( std::string const & extension ); Renvoie true si l'extension donnée est obsolète par une autre extension ou une version Vulkan.VULKAN_HPP_CONSTEXPR_20 bool isDeviceExtension( std::string const & extension ); Renvoie true si l'extension donnée est une extension de périphérique.VULKAN_HPP_CONSTEXPR_20 bool isInstanceExtension( std::string const & extension ); Renvoie true si l'extension donnée est une extension d'instance.VULKAN_HPP_CONSTEXPR_20 bool isObsoletedExtension( std::string const & extension ); Renvoie true si l'extension donnée est obsolète par une autre extension ou une version Vulkan.VULKAN_HPP_CONSTEXPR_20 bool isPromotedExtension( std::string const & extension ); Renvoie true si l'extension donnée est promue dans une autre extension ou une version Vulkan.Avertissement
La version actuelle de Microsoft Visual Studio 2022 n'est pas en mesure de gérer le module Vulkan.CPPM. Un bogue est déposé (https://developercommunity.visualstudio.com/t/on-building-a-c20-module:-fatal - error/10469799#t-nd10485943). Vous pouvez au moins utiliser cette fonctionnalité si vous n'avez pas besoin ou que vous ne souhaitez pas utiliser vk::UniqueHandle ou vk::SharedHandle en définissant VULKAN_HPP_NO_SMART_HANDLE .
Vulkan-HPP fournit un module nommé C ++, vulkan_hpp dans vulkan.cppm . Les modules C ++ sont destinés à remplacer les fichiers d'en-tête. Les modules ont le potentiel d'améliorer considérablement les temps de compilation pour les grands projets, car les déclarations et les définitions peuvent être facilement partagées entre les unités de traduction sans en-têtes d'analyse à plusieurs reprises. Vulkan-HPP a des en-têtes extrêmement longs (par exemple, vulkan_structs.hpp ), et le module C ++ est susceptible de raccourcir les temps de compilation pour les projets qui l'utilisent actuellement.
Cette fonctionnalité nécessite un compilateur récent avec un support complet C ++ 20:
cl.exe 19.28 ou version ultérieure)Si vous avez l'intention d'utiliser la prise en charge du module C ++ de CMake (et éventuellement ninja), des outils plus récents sont nécessaires:
cl.exe 19.34 ou plus tard)Avertissement
Le module nommé vulkan-HPP C ++ est toujours expérimental. Some suggested ways to use it in your projects are below. The long-term goal is to submit patches to the CMake FindVulkan module so that users may transparently configure the named module, without needing to declare it as an additional library in consumer CMake code.
CMake is recommended for use with the Vulkan-Hpp named module, as it provides a convenient platform-agnostic way to configure your project. CMake version 3.28 or later is required to support C++ modules. Refer to the CMake documentation on the topic.
CMake provides the FindVulkan module, which may be used to source the Vulkan SDK and Vulkan headers on your system.
# find Vulkan SDK
find_package ( Vulkan REQUIRED )
# Require Vulkan version ≥ 1.3.256 (earliest version when the Vulkan module was available)
if ( ${Vulkan_VERSION} VERSION_LESS "1.3.256" )
message ( FATAL_ERROR "Minimum required Vulkan version for C++ modules is 1.3.256. "
"Found ${Vulkan_VERSION} ."
)
endif ()
# set up Vulkan C++ module as a library
add_library ( VulkanHppModule )
target_sources ( VulkanHppModule PRIVATE
FILE_SET CXX_MODULES
BASE_DIRS ${Vulkan_INCLUDE_DIR}
FILES ${Vulkan_INCLUDE_DIR} /vulkan/vulkan.cppm
)
target_compile_features ( VulkanHppModule PUBLIC cxx_std_20 )
target_link_libraries ( VulkanHppModule PUBLIC Vulkan::Vulkan )
# link Vulkan C++ module into your project
add_executable ( YourProject main.cpp )
target_link_libraries ( YourProject PRIVATE VulkanHppModule ) Configuring the named module is straightforward; add any required Vulkan-Hpp feature macros (listed in Configuration Options) to target_compile_definitions . Par exemple:
# Disable exceptions, disable smart handles, disable constructors
target_compile_definitions ( VulkanHppModule PRIVATE
VULKAN_HPP_NO_EXCEPTIONS
VULKAN_HPP_NO_SMART_HANDLE
VULKAN_HPP_NO_CONSTRUCTORS
) It is important to have VULKAN_HPP_DISPATCH_LOADER_DYNAMIC defined equally for both the module and an importing project. To use the dynamic dispatcher, set it to 1 ; otherwise, leave it undefined or set it to 0 . In CMake, do this in a single line with target_compile_definitions and the PUBLIC scope:
target_compile_definitions ( VulkanHppModule PUBLIC
VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1
)
# ...
target_link_libraries ( YourProject PRIVATE VulkanHppModule ) Furthermore, you may also prefer linking VulkanHppModule to just the Vulkan::Headers target with the PUBLIC scope instead of Vulkan::Vulkan , so that the vulkan-1 library is not linked in, and the Vulkan headers are available to your consuming project, as detailed further below.
target_link_libraries ( VulkanHppModule PUBLIC Vulkan::Headers ) Finally, supply the macro VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE exactly once in your source code, just as in the non-module case. In order to have that macro available, include vulkan_hpp_macros.hpp , a lightweight header providing all Vulkan-Hpp related macros and defines. And as explained above, you need to initialize that dispatcher in two or three steps:
import vulkan_hpp;
# include < vulkan/vulkan_hpp_macros.hpp >
# if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
# endif
auto main ( int argc, char * const argv[]) -> int
{
# if ( VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 )
// initialize minimal set of function pointers
VULKAN_HPP_DEFAULT_DISPATCHER. init ();
# endif
auto appInfo = vk::ApplicationInfo ( " My App " , 1 , " My Engine " , 1 , vk::makeApiVersion ( 1 , 0 , 0 , 0 ));
// ...
} An example is provided in tests/Cpp20Modules/Cpp20Modules.cpp .
Finally, you can configure and build your project as usual. Note that CMake currently only supports the Ninja and Visual Studio generators for C++ modules.
If you want to use the Vulkan-Hpp C++ module without CMake, you must first pre-compile it, and then import it into your project. You will also need to define any macros that control various features of Vulkan-Hpp, such as VULKAN_HPP_NO_EXCEPTIONS and VULKAN_HPP_NO_SMART_HANDLE . Different compilers have different command-lines for module pre-compilation; however, for initial use, some examples are provided below, assuming the same main.cpp consumer as above.
For MSVC, source vcvars64.bat or use a Developer Command Prompt/Developer PowerShell instance, and run the following:
cl.exe /std:c++20 /interface /TP < path-to-vulkan-hpp > vulkan.cppm
cl.exe /std:c++20 /reference vulkan=vulkan.ifc main.cpp vulkan.obj
.main.exeFor Clang, run the following:
clang++ -std=c++20 < path-to-vulkan-hpp > /vulkan.cppm -precompile -o vulkan.pcm
clang++ -std=c++20 -fprebuilt-module-path=. main.cpp vulkan.pcm -o main
./mainMore information about module compilation may be found at the respective compiler's documentation:
When you configure your project using CMake, you can enable SAMPLES_BUILD to add some sample projects to your solution. Most of them are ports from the LunarG samples, but there are some more, like CreateDebugUtilsMessenger, InstanceVersion, PhysicalDeviceDisplayProperties, PhysicalDeviceExtensions, PhysicalDeviceFeatures, PhysicalDeviceGroups, PhysicalDeviceMemoryProperties, PhysicalDeviceProperties, PhysicalDeviceQueueFamilyProperties, and RayTracing. All those samples should just compile and run. When you configure your project using CMake, you can enable TESTS_BUILD to add some test projects to your solution. Those tests are just compilation tests and are not required to run.
As vulkan.hpp is pretty big, some compilers might need some time to digest all that stuff. In order to potentially reduce the time needed to compile that header, a couple of defines will be introduced, that allow you to hide certain features. Whenever you don't need that corresponding feature, defining that value might improve your compile time. Currently, there are just a couple of such defines:
VULKAN_HPP_NO_SPACESHIP_OPERATOR , which removes the spaceship operator on structures (available with C++20)VULKAN_HPP_NO_TO_STRING , which removes the various vk::to_string functions on enums and bitmasks.VULKAN_HPP_USE_REFLECT , this one needs to be defined to use the reflection function on structures. It's very slow to compile, though! As Vulkan-Hpp often needs to switch between C++ vk-types and corresponding bit-identical C-types, using reinterpret_cast , it is highly recommended to use the compile option -fno-strict-aliasing to prevent potentially breaking compile optimizations.
There are a couple of defines you can use to control the feature set and behaviour of vulkan.hpp :
At various places in vulkan.hpp an assertion statement is used. By default, the standard assert funtions from <cassert> is called. By defining VULKAN_HPP_ASSERT before including vulkan.hpp , you can change that to any function with the very same interface.
If there are no exceptions enabled (see VULKAN_HPP_NO_EXCEPTIONS ), an assertion statement checks for a valid success code returned from every vulkan call. By default, this is the very same assert function as defined by VULKAN_HPP_ASSERT , but by defining VULKAN_HPP_ASSERT_ON_RESULT you can replace just those assertions with your own function, using the very same interface.
Every vk-function gets a Dispatcher as its very last argument, which defaults to VULKAN_HPP_DEFAULT_DISPATCHER . If VULKAN_HPP_DISPATCH_LOADER_DYNAMIC is defined to be 1 , it is defaultDispatchLoaderDynamic . This in turn is the dispatcher instance, which is defined by VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE , which has to be used exactly once in your sources. If, on the other hand, VULKAN_HPP_DISPATCH_LOADER_DYNAMIC is defined to something different from 1 , VULKAN_HPP_DEFAULT_DISPATCHER is set to be DispatchLoaderStatic() . You can use your own default dispatcher by setting VULKAN_HPP_DEFAULT_DISPATCHER to an object that provides the same API. If you explicitly set VULKAN_HPP_DEFAULT_DISPATCHER , you need to set VULKAN_HPP_DEFAULT_DISPATCHER_TYPE accordingly as well.
This names the default dispatcher type, as specified by VULKAN_HPP_DEFAULT_DISPATCHER . Per default, it is DispatchLoaderDynamic or DispatchLoaderStatic, depending on VULKAN_HPP_DISPATCH_LOADER_DYNAMIC being 1 or not 1 , respectively. If you explicitly set VULKAN_HPP_DEFAULT_DISPATCHER , you need to set VULKAN_HPP_DEFAULT_DISPATCHER_TYPE accordingly as well.
If you have not defined your own VULKAN_HPP_DEFAULT_DISPATCHER , and have VULKAN_HPP_DISPATCH_LOADER_DYNAMIC defined to be 1 (the default), you need to have the macro VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE exactly once in any of your source files to provide storage for that default dispatcher. VULKAN_HPP_STORAGE_API then controls the import/export status of that default dispatcher.
When this is defined before including vulkan.hpp , you essentially disable all enhanced functionality. All you then get is:
vk::Flags for bitmasks;vk::StructureChain for compile-time construction of structure chains.If this is not defined, you additionally get:
vk::ArrayProxy<> ), simplifying handling of array data; returning requested data; throwing exceptions on errors (as long as VULKAN_HPP_NO_EXCEPTIONS is not defined);VULKAN_HPP_NO_STRUCT_CONSTRUCTORS is not defined) (consuming vk::ArrayProxyNoTemporaries<> );vk::ArrayProxyNoTemporaries<> );vk::ArrayProxy<> and vk::ArrayProxyNoTemporaries<>vulkan_raii.hpp This either selects the dynamic (when it's 1 ) or the static (when it's not 1 ) DispatchLoader as the default one, as long as it's not explicitly specified by VULKAN_HPP_DEFAULT_DISPATCHER . By default, this is defined to be 1 if VK_NO_PROTOTYPES is defined, otherwise 0 .
By default, a little helper class DynamicLoader is used to dynamically load the vulkan library. If you set it to something different than 1 before including vulkan.hpp , this helper is not available, and you need to explicitly provide your own loader type for the function DispatchLoaderDynamic::init() .
When this is not externally defined and VULKAN_HPP_CPP_VERSION is at least 23 , VULKAN_HPP_EXPECTED is defined to be std::expected , and VULKAN_HPP_UNEXPECTED is defined to be std::unexpected .
By default, the member m_mask of the Flags class template is private. This is to prevent accidentally setting a Flags with some inappropriate value. But it also prevents using a Flags , or a structure holding a Flags , to be used as a non-type template parameter. If you really need that functionality, and accept the reduced security, you can use this define to change the access specifier for m_mask from private to public, which allows using a Flags as a non-type template parameter.
This define can be used to enable m_handle = exchange( rhs.m_handle, {} ) in move constructors of Vulkan-Hpp handles, which default-initializes the rhs underlying value. By default Vulkan-Hpp handles behave like trivial types -- move constructors copying value.
This define can be used to specify your own hash combiner function. In order to determine the hash of a vk-structure, the hashes of the members of that struct are to be combined. This is done by this define, which by default is identical to what the function boost::hash_combine() does. It gets the type of the to-be-combined value, the seed, which is the combined value up to that point, and finally the to-be-combined value. This hash calculation determines a "shallow" hash, as it takes the hashes of any pointer in a structure, and not the hash of a pointed-to value.
This is set to be the compiler-dependent attribute used to mark functions as inline. If your compiler happens to need some different attribute, you can set this define accordingly before including vulkan.hpp .
By default, the namespace used with vulkan.hpp is vk . By defining VULKAN_HPP_NAMESPACE before including vulkan.hpp , you can adjust this.
By default, the file vulkan_to_string.hpp is included by vulkan.hpp and provides functions vk::to_string for enums and bitmasks. If you don't need those functions, you can define VULKAN_HPP_NO_TO_STRING to prevent that inclusion. If you have certain files where you want to use those functions nevertheless, you can explicitly include vulkan_to_string.hpp there.
With C++20, designated initializers are available. Their use requires the absence of any user-defined constructors. Define VULKAN_HPP_NO_CONSTRUCTORS to remove constructors from structs and unions.
When a vulkan function returns an error code that is not specified to be a success code, an exception is thrown unless VULKAN_HPP_NO_EXCEPTIONS is defined before including vulkan.hpp .
With C++17, all vk-functions returning something are declared with the attribute [[nodiscard]] . This can be removed by defining VULKAN_HPP_NO_NODISCARD_WARNINGS before including vulkan.hpp .
By defining VULKAN_HPP_NO_SETTERS before including vulkan.hpp , setter member functions will not be available within structs and unions. Modifying their data members will then only be possible via direct assignment.
By defining VULKAN_HPP_NO_SMART_HANDLE before including vulkan.hpp , the helper class vk::UniqueHandle and all the unique handle types are not available.
With C++20, the so-called spaceship-operator <=> is introduced. If that operator is supported, all the structs and classes in vulkan.hpp use the default implementation of it. As currently some implementations of this operator are very slow, and others seem to be incomplete, by defining VULKAN_HPP_NO_SPACESHIP_OPERATOR before including vulkan.hpp you can remove that operator from those structs and classes.
By default, if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL is enabled on Win32, vulkan.hpp declares HINSTANCE , LoadLibraryA , and other required symbols. It could cause conflicts with the Windows.h alternatives, such as WindowsHModular . With this define, you can disable these declarations, but you will have to declare them before the vulkan.hpp is included.
If both, VULKAN_HPP_NO_EXCEPTIONS and VULKAN_HPP_EXPECTED are defined, the vk::raii-classes don't throw exceptions. That is, the actual constructors are not available, but the creation-functions must be used. For more details have a look at the vk_raii_ProgrammingGuide.md .
Even though vk::UniqueHandle and vk::SharedHandle are semantically close to pointers, an implicit cast operator to the underlying vk::Handle might be handy. You can add that implicit cast operator by defining VULKAN_HPP_SMART_HANDLE_IMPLICIT_CAST .
With this define you can specify whether the DispatchLoaderDynamic is imported or exported (see VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE ). If VULKAN_HPP_STORAGE_API is not defined externally, and VULKAN_HPP_STORAGE_SHARED is defined, depending on the VULKAN_HPP_STORAGE_SHARED_EXPORT being defined, VULKAN_HPP_STORAGE_API is either set to __declspec( dllexport ) (for MSVC) / __attribute__( ( visibility( "default" ) ) ) (for gcc or clang) or __declspec( dllimport ) (for MSVC), respectively. For other compilers, you might specify the corresponding storage by defining VULKAN_HPP_STORAGE_API on your own.
32-bit vulkan is not typesafe for non-dispatchable handles, so we don't allow copy constructors on this platform by default. To enable this feature on 32-bit platforms, #define VULKAN_HPP_TYPESAFE_CONVERSION 1 . To disable this feature on 64-bit platforms, #define VULKAN_HPP_TYPESAFE_CONVERSION 0 .
See VULKAN_HPP_EXPECTED .
With this define you can include a reflection mechanism on the vk-structures. It adds a function reflect that returns a tuple-version of the structure. That tuple then could easily be iterated. But at least for now, that feature takes lots of compile-time resources, so currently it is recommended to enable that feature only if you're willing to pay that price.
Feel free to submit a PR to add to this list.
Copyright 2015-2020 The Khronos Group Inc.
Licencié sous la licence Apache, version 2.0 (la "licence"); Vous ne pouvez pas utiliser ce fichier sauf conforme à la licence. Vous pouvez obtenir une copie de la licence à
http://www.apache.org/licenses/LICENSE-2.0
Sauf exiger la loi applicable ou convenu par écrit, les logiciels distribués en vertu de la licence sont distribués sur une base «tel quel», sans garantie ou conditions d'aucune sorte, expresse ou implicite. Voir la licence pour la langue spécifique régissant les autorisations et les limitations sous la licence.