El objetivo del Vulkan-HPP es proporcionar solo el encabezado de enlaces de C ++ para la API Vulkan C para mejorar la experiencia de los desarrolladores vulkan sin introducir el costo de tiempo de ejecución de CPU. Agrega características como Tipo de seguridad para Enums y Bitfields, soporte de contenedores STL, excepciones y enumeraciones simples.
| Plataforma | Estado de construcción |
|---|---|
| Linux |
Vulkan-HPP es parte del Lunarg Vulkan SDK desde la versión 1.0.24. Simplemente #include <vulkan/vulkan.hpp> y estará listo para usar los enlaces C ++. Si está utilizando una versión de Vulkan aún no compatible con el Vulkan SDK, puede encontrar la última versión de los encabezados aquí.
Vulkan-HPP requiere un compilador capaz de C ++ 11 para compilar. Se sabe que los siguientes compiladores funcionan:
Para construir las muestras y pruebas locales, tendrá que clonar este repositorio y ejecutar cmake para generar los archivos de compilación requeridos
Instalar dependencias.
Abra un shell que proporcione git y clone el repositorio con:
git clone --recurse-submodules https://github.com/KhronosGroup/Vulkan-Hpp.git
Cambie el directorio actual al directorio Vulkan-HPP recién creado.
Cree un entorno de compilación con 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
Es posible que deba especificar un generador a través -G , para una lista completa de generadores ejecutar cmake -G .
vulkan.hpp del archivo de registro XML vk.xml , agregue la opción -DVULKAN_HPP_RUN_GENERATOR=ON a la línea de comandos CMake. Abra el proyecto generado con un IDE, por ejemplo, Visual Studio o inicie el proceso de compilación con cmake --build build --parallel .
Opcional: para actualizar el Vulkan-HPP y sus submódulos ejecutan git pull --recurse-submodules .
Puede descargar e instalar Vulkan-HPP utilizando el Administrador de dependencias VCPKG:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install vulkan-headersEl puerto Vulkan-HPP en VCPKG se mantiene actualizado por los miembros del equipo de Microsoft y los contribuyentes comunitarios. Si la versión está desactualizada, cree un problema o extraiga la solicitud en el repositorio de VCPKG.
Si CMake encuentra el programa CLANG-Format, la define CLANG_FORMAT_EXECUTABLE se establece en consecuencia. En ese caso, el vulkan.hpp generado se formateado utilizando el archivo .clang-format ubicado en el directorio raíz de este proyecto. De lo contrario, está formateado como codificado en el generador.
El archivo VulkanHpp.natvis proporciona una vista personalizada en vk::Flags para Visual Studio. Si agrega este archivo al directorio NATVIS específico del usuario de su instalación de Visual Studio (%userProfile% Documents Visual Studio 2022 Visualizers), obtiene vk::Flags bien formateadas en su depurador con todos sus proyectos de Visual Studio.
Para evitar colisiones de nombres con la API Vulkan C, los enlaces de C ++ residen en el espacio de nombres vk . Las siguientes reglas se aplican al nuevo nombre:
Vk eliminado. Además de esto, la primera letra de funciones es minúscula.vkCreateInstance como vk::createInstance .VkImageTiling como vk::ImageTiling .VkImageCreateInfo como vk::ImageCreateInfo .VK_ y el tipo de infijo eliminado. Si el tipo enum es una extensión, el sufijo de extensión se ha eliminado de los valores de enum.En todos los demás casos, el sufijo de extensión no se ha eliminado.
VK_IMAGETYPE_2D ahora es vk::ImageType::e2D .VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ahora es vk::ColorSpaceKHR::eSrgbNonlinear .VK_STRUCTURE_TYPE_PRESENT_INFO_KHR ahora es vk::StructureType::ePresentInfoKHR ._BIT también se ha eliminado. En algunos casos, podría ser necesario mover Vulkan-HPP a un espacio de nombres personalizado. Esto se puede lograr definiendo VULKAN_HPP_NAMESPACE antes de incluir a Vulkan-HPP.
Vulkan-HPP declara una clase para todos los mangos para garantizar la seguridad de tipo completo y agregar soporte para las funciones de los miembros en las manijas. Se ha agregado una función miembro a una clase de manejo para cada función que acepta el mango correspondiente como primer parámetro. En lugar de vkBindBufferMemory(device, ...) vk::bindBufferMemory(device, ...) puede escribir device.bindBufferMemory(...) .
Hay un encabezado adicional llamado vulkan_raii.hpp generado. Ese encabezado contiene las clases de envoltura que cumplen con RAII para los tipos de mango. Es decir, por ejemplo, el tipo de mango VkInstance , hay un envoltorio que cumple con RAII vk::raii::Instance . Eche un vistazo a las muestras usando esas clases en el directorio RAII_SAMPLES.
En plataformas de 64 bits, Vulkan-HPP admite conversiones implícitas entre las manijas de Vulkan C ++ y las manijas de C Vulkan. En las plataformas de 32 bits, todas las manijas no disputables se definen como uint64_t , evitando así las verificaciones de conversión de tipo en el tiempo de compilación que captarían las tareas entre los tipos de manija incompatibles. Debido a que Vulkan-HPP no habilita la conversión implícita para plataformas de 32 bits de forma predeterminada y se recomienda utilizar un static_cast para la conversión como esta: VkImage = static_cast<VkImage>(cppImage) para evitar la conversión de un mango arbitrary int to a un mango o viceversa por accidente. Si está desarrollando su código en una plataforma de 64 bits, pero desea compilar su código para una plataforma de 32 bits sin agregar las emergencias explícitas, puede definir VULKAN_HPP_TYPESAFE_CONVERSION a 1 en su sistema de compilación o antes de incluir vulkan.hpp . En las plataformas de 64 bits, esto define se establece en 1 de forma predeterminada y se puede establecer en 0 para deshabilitar las conversiones implícitas.
La función de ENUM alcanzada agrega seguridad de tipo a los indicadores, pero también evita el uso de los bits de bandera como entrada para operaciones bit a bits como & y | .
Como solución, Vulkan-HPP proporciona una plantilla de clase vk::Flags que trae las operaciones estándar como &= , |= , & y | a nuestros enums con alcance. Excepto por la inicialización con 0 esta clase se comporta exactamente como una masa de bits normal con la mejora de que es imposible establecer bits no especificados por el enumio correspondiente por accidente. Aquí hay algunos ejemplos para el manejo de la masa de bits:
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 */ , ...); Al construir un mango en Vulkan, uno generalmente tiene que crear una estructura CreateInfo que describe el nuevo mango. Esto puede dar lugar a un código bastante largo como se puede ver en el siguiente ejemplo de Vulkan C:
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); Hay dos problemas típicos que los desarrolladores de Vulkan encuentran al completar un campo CreateInfo STRTUC por campo:
sType es incorrecto.Especialmente el primero es difícil de detectar.
Vulkan-HPP proporciona constructores para todos los objetos CreateInfo que aceptan un parámetro para cada variable miembro. De esta manera, el compilador arroja un error del compilador si se ha olvidado un valor. Además de este sType , se llena automáticamente con el valor correcto y pNext establecido en un nullptr de forma predeterminada. Así es como se ve el mismo código con un constructor:
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); Con constructores para las estructuras CreateInfo , uno también puede pasar temporalmente a funciones de Vulkan como esta:
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}); Comenzando con C ++ 20, C ++ admite inicializadores designados. Como esa característica requiere no tener constructores de usuario o heredados de usuario, debe #define VULKAN_HPP_NO_CONSTRUCTORS , que elimina toda la estructura y los constructores sindicales de vulkan.hpp . En su lugar, puede usar la inicialización agregada. Las primeras líneas de VK en su fuente pueden verse como:
// 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 };en lugar 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); Tenga en cuenta que el orden del designador debe coincidir con la orden de declaración. Tenga en cuenta también, que ahora puede establecer explícitamente el miembro sType de VK-Structures. Esto no es necesario (ya que se inicializan correctamente por defecto) ni se recomienda.
La API de Vulkan tiene varios lugares que requieren (cuenta, puntero), ya que dos argumentos de función y C ++ tiene algunos contenedores que se asignan perfectamente a este par. Para simplificar el desarrollo, los enlaces Vulkan-HPP han reemplazado esos pares de argumentos con la plantilla de clase vk::ArrayProxy que acepta matrices vacías y un solo valor, así como contenedores STL std::initializer_list , std::array and std::vector como argumento para la construcción. De esta manera, una sola versión de Vulkan generada puede aceptar una variedad de entradas sin tener la explosión combinatoria que ocurriría al crear una función para cada tipo de contenedor.
Aquí hay algunas muestras de código sobre cómo usar el 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 genera referencias para punteros a estructuras. Esta conversión permite pasar estructuras temporales a funciones que pueden dar lugar a un código más corto. En caso de que la entrada sea opcional y, por lo tanto, acepte un puntero nulo, el tipo de parámetro será vk::Optional<T> const& . Este tipo acepta una referencia a T o nullptr como entrada y, por lo tanto, permite estructuras temporales opcionales.
// 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 permite encadenar estructuras a través del puntero pNext . Vulkan-HPP tiene una plantilla de clase variádica que permite la construcción de tales cadenas de estructura con esfuerzos mínimos. Además de esto, se verifica en el tiempo de compilación si la especificación permite la construcción de dicha cadena 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 proporciona un constructor para estas cadenas similares a los objetos CreateInfo que acepta una lista de todas las estructuras parte de la cadena. El campo pNext se establece automáticamente en el valor correcto:
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> c = {
vk::MemoryAllocateInfo (size, type),
vk::MemoryDedicatedAllocateInfo (image)
}; Si se debe eliminar una de las estructuras de una estructura de estructura, tal vez debido a algunas configuraciones opcionales, puede usar la función vk::StructureChain::unlink<ClassType>() . Modifica la estructura de tal manera que la estructura especificada ya no es parte de la cadena de pnext. Tenga en cuenta que el diseño de memoria real de StructureChain no está modificado por esa función. En caso de que esa misma estructura tenga que volver a agregar a StructureChain nuevamente, use vk::StructureChain::relink<ClassType>() .
A veces, el usuario tiene que pasar una cadena de estructura preellocada para consultar la información. Para esos casos hay dos funciones de Getter correspondientes. Uno con una plantilla variádica que genera una cadena de estructura de al menos dos elementos para construir el valor de retorno:
// 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>();Para obtener solo la estructura base, sin encadenar, la otra función Getter proporcionada no necesita un argumento de plantilla para que la estructura obtenga:
// Query just vk::MemoryRequirements2KHR
vk::MemoryRequirements2KHR memoryRequirements = device.getBufferMemoryRequirements2KHR({}); Por defecto, Vulkan-HPP tiene excepciones habilitadas. Esto significa que Vulkan-HPP verifica el código de retorno de cada llamada de función que devuelve un vk::Result . Si vk::Result es una falla, se lanzará un std::runtime_error . Dado que ya no es necesario devolver el código de error, los enlaces C ++ ahora pueden devolver el valor de retorno deseado real, es decir, un mango Vulkan. En esos casos vk::ResultValue<SomeType>::type se define como el tipo devuelto.
Para crear un dispositivo, ahora puede escribir:
vk::Device device = physicalDevice.createDevice(createInfo); Algunas funciones permiten más que solo vk::Result::eSuccess para ser considerado como un código de éxito. Para esas funciones, siempre devolvemos un vk::ResultValue<SomeType> . Un ejemplo es acquireNextImage2KHR , que se puede usar así:
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 ;
} A medida que pasa el tiempo, algunas funciones de Vulkan pueden cambiar, de modo que comienzan a admitir más códigos de resultados que vk::Result::eSuccess como un código de éxito. Ese cambio lógico no sería visible en la API C, sino en la API C ++, como tal función ahora devolvería un vk::ResultValue<SomeType> en lugar de solo SomeType . En tales casos (raros), tendría que ajustar sus fuentes CPP para reflejar ese cambio de API.
Si el manejo de excepciones se deshabilita definiendo VULKAN_HPP_NO_EXCEPTIONS el tipo de vk::ResultValue<SomeType>::type es una estructura que contiene un vk::Result y un SomeType . Esta estructura admite desempacar los valores de retorno utilizando std::tie .
En caso de que no desee usar la transformación vk::ArrayProxy y el valor de retorno, aún puede llamar a la función de estilo C simple. A continuación hay tres ejemplos que muestran las 3 formas de usar la API:
El primer fragmento muestra cómo usar la API sin excepciones y la transformación del valor de retorno:
// 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 ?
}El segundo fragmento muestra cómo usar la API usando la transformación del valor de retorno, pero sin excepciones. Ya es un poco más corto que el código original:
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 ?
}Una mejor manera de desempaquetar el resultado es utilizar enlaces estructurados en C ++ 17. Nos permitirán obtener el resultado con una sola línea de código:
auto [result, shaderModule2] = device.createShaderModule({...} /* createInfo temporary */ );Finalmente, el último ejemplo de código es usar excepciones y transformación del valor de retorno. Este es el modo predeterminado de la API.
vk::ShaderModule shader1;
vk::ShaderModule shader2;
try
{
shader1 = device. createShaderModule ({...});
shader2 = device. createShaderModule ({...});
}
catch (std:: exception const &e)
{
// handle error and free resources
}Importante
Las manejas de Vulkan en el vk -NamesSpace no admiten RAII, por lo tanto, debe limpiar sus recursos en el controlador de errores. En su lugar, puede usar las clases de envoltorio de identificación en el espacio de Names vk::raii .
Con C ++ 17 y superior, algunas funciones se atribuyen con [[nodiscard]] , lo que resulta en una advertencia si no usa el valor de retorno de alguna manera. Puede desconectar esas advertencias definiendo VULKAN_HPP_NO_NODISCARD_WARNINGS .
Para la transformación del valor de retorno, hay una clase especial de valores de retorno que requieren un manejo especial: enumeraciones. Para las enumeraciones, generalmente debe escribir un código como este:
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);Dado que escribir este bucle una y otra vez es tedioso y propenso a los errores, el enlace de C ++ se encarga de la enumeración para que pueda escribir:
std::vector<LayerProperties> properties = physicalDevice.enumerateDeviceLayerProperties(); Vulkan-HPP proporciona una interfaz vk::UniqueHandle<Type, Deleter> . Para cada tipo de mango de Vulkan, tipo vk::Type hay un mango único vk::UniqueType que eliminará el recurso vulkan subyacente tras la destrucción, por ejemplo vk::UniqueBuffer es el mango único para vk::Buffer .
Para cada función que construye un mango Vulkan de Tipo vk::Type Vulkan-HPP proporciona una segunda versión que devuelve un vk::UniqueType . Por ejemplo, para vk::Device::createBuffer Hay vk::Device::createBufferUnique y para vk::allocateCommandBuffers Hay vk::allocateCommandBuffersUnique .
Tenga en cuenta que usar vk::UniqueHandle tiene un costo ya que la mayoría de los eliminadores tienen que almacenar el vk::AllocationCallbacks y el mango de los padres utilizados para la construcción porque son necesarios para la destrucción automática.
Vulkan-HPP proporciona una interfaz vk::SharedHandle<Type> . Para cada mango de Vulkan tipo vk::Type Hay un mango compartido vk::SharedType que eliminará el recurso Vulkan subyacente tras la destrucción, por ejemplo vk::SharedBuffer es el mango compartido para vk::Buffer .
A diferencia de vk::UniqueHandle , vk::SharedHandle toma la propiedad compartida del recurso y su padre. Esto significa que el mango de los padres no será destruido hasta que se eliminen todos los recursos infantiles. Esto es útil para los recursos que se comparten entre múltiples hilos u objetos.
Este mecanismo asegura un orden de destrucción correcto, incluso si el padre vk::SharedHandle se destruye antes de que su hijo maneje. De lo contrario, el mango se comporta como std::shared_ptr . vk::SharedInstance o cualquiera de su objeto infantil debe ser el último para eliminar (primero creado, primero en la declaración de clase).
No hay funciones que devuelvan un vk::SharedHandle directamente todavía. En su lugar, puede construir un vk::SharedHandle desde un vk::Handle :
vk::Buffer buffer = device.createBuffer(...);
vk::SharedBuffer sharedBuffer (buffer, device); // sharedBuffer now owns the buffer Hay varias especializaciones de vk::SharedHandle para diferentes tipos de mangos. Por ejemplo, vk::SharedImage puede tomar un argumento adicional para especificar si la imagen es propiedad de 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 También hay una especialización para vk::SwapchainKHR que toma un argumento adicional para especificar una superficie:
vk::SwapchainKHR swapchain = device.createSwapchainKHR(...);
vk::SharedSwapchainKHR sharedSwapchain (swapchain, device, surface); // sharedSwapchain now owns the swapchain and surface Puede crear una sobrecarga vk::SharedHandle para su propio tipo de mango o mangos compartidos propios proporcionando varios argumentos de plantilla a SharedHandleBase :
Con esto, proporcione una función de destrucción estática personalizada internalDestroy , que toma un mango padre y un mango para destruir. No olvides agregar una declaración de amigos para la clase 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);
}
};La API se extenderá para proporcionar funciones de creación en el futuro.
Además de vk::UniqueHandles y vk::SharedHandles , hay un conjunto de clases de envoltorio para todos los tipos de mango que siguen el RAII-Paradigm (la adquisición de recursos es inicialización), proporcionada en el espacio de nombres vk::raii .
Mientras que un vk::UniqueHandle imita un mango envuelto por un std::unique_ptr , y un vk::SharedHandle imita un mango envuelto por un std::shared_ptr , incluida la información de los padres, un vk::raii::Handle es solo una clase que adquiere el VK-desconectado en su construcción y se lanza en su destructor. Por lo tanto, eres libre de usarlos como valores o envolverlos con un puntero inteligente.
Aparte de las vk::Handles , todas esas clases de envoltura de manejo deben contener datos adicionales y, por lo tanto, no son idénticos binarios con los Handles C Vulkan.
Como el vk::UniqueHandles y el vk::SharedHandles usa el mismo despachador que el vk::Handles , se pueden mezclar y combinar fácilmente. ¡ vk::raii::Handles Use algunos despachadores ligeramente diferentes y, por lo tanto, no son compatibles con las otras manijas! Es decir, para los vk-Handles , el vk::UniqueHandles , y el vk::SharedHandles , debe instanciar a un despachador global como se describe en https://github.com/khronosgroup/vulkan-hpp#Extensions-Perdedevice-Function-pointers. Para el vk::raii-Handles , esto no es necesario, ya que mantienen a sus propios despachadores. La gran ventaja aquí es cuando tiene múltiples dispositivos: las funciones llamadas a través de vk::raii-Handles siempre llaman a las funciones específicas del dispositivo.
A veces se requiere usar std::vector con asignadores personalizados. Vulkan-HPP admite vectores con asignadores personalizados como entrada para vk::ArrayProxy y para funciones que devuelven un vector. Para el último caso, agregue su asignador personalizado favorito como argumento de plantilla a la llamada de función como esta:
std::vector<LayerProperties, MyCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties<MyCustomAllocator>(); También puede usar un asignador personalizado con estado proporcionándolo como argumento a esas funciones. Desafortunadamente, para hacer felices a los compiladores, también debe establecer explícitamente el argumento de despacho. Para obtener el valor predeterminado allí, un simple {} sería suficiente:
MyStatefulCustomAllocator allocator;
std::vector<LayerProperties, MyStatefulCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties(allocator, {}); En todo vulkan.hpp , hay un par de llamadas a una función de afirmación. Al definir VULKAN_HPP_ASSERT , puede especificar su propia función de afirmación personalizada que se llamará en su lugar.
De forma predeterminada, se utilizará VULKAN_HPP_ASSERT_ON_RESULT para verificar los resultados cuando se define VULKAN_HPP_NO_EXCEPTIONS . Si desea manejar los errores usted mismo, puede deshabilitarlo/personalizarlo como VULKAN_HPP_ASSERT .
Hay un par de afirmaciones estáticas para cada clase de manejo y cada estructura en el archivo vulkan_static_assertions.hpp . Puede incluir ese archivo en al menos uno de sus archivos de origen. Al definir VULKAN_HPP_STATIC_ASSERT , puede especificar su propia afirmación estática personalizada que se utilizará para esos casos. Es decir, al definirlo como un NOP, puede reducir un poco su tiempo de compilación.
El cargador Vulkan expone solo las funciones centrales de Vulkan y un número limitado de extensiones. Para usar Vulkan-HPP con extensiones, se requiere tener una biblioteca que proporcione trozos a todas las funciones de Vulkan usados o para decirle a Vulkan-HPP que envíe esos consejos de funciones. Vulkan-HPP proporciona un mecanismo de envío por función al aceptar una clase de despacho como último parámetro en cada llamada de función. La clase de despacho debe proporcionar un tipo de llamabilidad para cada función Vulkan usada. Vulkan-HPP proporciona una implementación, DispatchLoaderDynamic , que obtiene todos los consejos de función conocidos por la biblioteca.
// 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); Para usar el vk::DispatchLoaderDynamic como el despachador predeterminado (significa: no necesita agregarlo explícitamente a cada llamada de función), debe #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 , y tener el macro VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE exactamente una vez en su código fuente para proporcionar el almacenamiento para el almacenamiento predeterminado para el envoltor predeterminado. Luego puede usarlo por Macro VULKAN_HPP_DEFAULT_DISPATCHER , como se muestra en los fragmentos de código a continuación. Crear un vk::DispatchLoaderDynamic con todo el mundo es un proceso de dos a tres pasos, donde tiene tres opciones para el primer paso:
vk::DynamicLoader : VULKAN_HPP_DEFAULT_DISPATCHER.init();getProcAddress (compare con vk::DynamicLoader en vulkan.hpp ): YourDynamicLoader ydl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(ydl);Nota
Debe mantener vivo ese objeto de cargador dinámico hasta después de la última llamada a una función Vulkan en su programa. Por ejemplo, haciéndolo estático, o almacenándolo a nivel mundial.
PFN_vkGetInstanceProcAddr : PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = your_own_function_pointer_getter();
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);vk::Instance para obtener todos los demás punteros de la función: vk::Instance instance = vk::createInstance({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);vk::Device para obtener punteros de función específicos del dispositivo std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
assert (!physicalDevices.empty());
vk::Device device = physicalDevices[ 0 ].createDevice({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(device);Después del segundo paso anterior, el despachador es completamente funcional. Agregar el tercer paso puede dar como resultado un código más eficiente. Pero si tiene la intención de usar múltiples dispositivos, puede omitir ese tercer paso y dejar que el controlador haga el disco del dispositivo.
En algunos casos, el almacenamiento para el DispathLoaderDynamic debe integrarse en una DLL. Para esos casos, debe definir VULKAN_HPP_STORAGE_SHARED para decirle a Vulkan-HPP que el almacenamiento reside en una DLL. Al compilar el DLL con el almacenamiento, también se requiere para definir VULKAN_HPP_STORAGE_SHARED_EXPORT para exportar los símbolos requeridos.
Para todas las funciones, ese VULKAN_HPP_DEFAULT_DISPATCHER es el valor predeterminado para el último argumento a esa función. Si desea proporcionar explícitamente al despachador para cada llamada de función (cuando tiene múltiples despachadores para diferentes dispositivos, por ejemplo) y desea asegurarse de que no se pierda accidentalmente ninguna llamada de función, puede definir VULKAN_HPP_NO_DEFAULT_DISPATCHER antes de incluir vulkan.hpp para eliminar ese argumento predeterminado.
vulkan.hpp proporciona un par de rasgos de tipo, aliviando la metaprogramación de la plantilla:
template <typename EnumType, EnumType value> struct CppType maps valores de IndexType ( IndexType::eUint16 , IndexType::eUint32 , ...) al tipo correspondiente ( uint16_t , uint32_t , ...) por el Type de miembro; Mapas ObjectType Values ( ObjectType::eInstance , ObjectType::eDevice , ...) al tipo correspondiente ( vk::Instance , vk::Device , ...) por el Type de miembro del miembro; Maps DebugReportObjectType Values ( DebugReportObjectTypeEXT::eInstance , DebugReportObjectTypeEXT::eDevice , ...) al tipo correspondiente ( vk::Instance , vk::Device , ...) por el Type de miembro;template <typename T> struct IndexTypeValue tipos escalares ( uint16_t , uint32_t , ...) al valor IndexType correspondiente ( IndexType::eUint16 , IndexType::eUint32 , ...).template <typename T> struct isVulkanHandleType mapea un tipo a true si y solo si es una clase de manejo ( vk::Instance , vk::Device , ...) por el value del miembro estático.HandleClass::CType Maps Una clase de identificación ( vk::Instance , vk::Device , ...) al tipo C correspondiente ( VkInstance , VkDevice , ...) por el tipo de miembro CType .HandleClass::objectType maps a una clase de identificación ( vk::Instance , vk::Device , ...) al valor de ObjectType correspondiente ( ObjectType::eInstance , ObjectType::eDevice , ...) por el miembro estático objectType .HandleClass::debugReportObjectType Maps A Clase de Handle ( vk::Instance , vk::Device , ...) AL VALOR DE DebugReportObjectTypeEXT COR CAENTENTAL ( DebugReportObjectTypeEXT::eInstance , DebugReportObjectTypeEXT::eDevice , ...) por el miembro estático debugReportObjectType . Con el encabezado adicional vulkan_format_traits.hpp , se proporcionan un par de funciones de rasgos en vk::Format . Con C ++ 14 y más, todas esas funciones están marcadas como constexpr , es decir, con los argumentos apropiados, se resuelven en el momento de la compilación.
uin8_t blockSize( vk::Format format ); Obtiene el tamaño del bloque Texel de este formato en bytes.uint8_t texelsPerBlock( vk::Format format ); Obtiene el número de Texels en un bloque Texel.std::array<uint8_t, 3> blockExtent( vk::Format format ); Obtiene la extensión tridimensional de los bloques de Texel.char const * compressionScheme( vk::Format format ); Obtiene una descripción textual del esquema de compresión de este formato, o un texto vacío si no está comprimido.bool isCompressed( vk::Format format ); Verdadero, si el formato es un formato comprimido, de lo contrario falso.uint8_t packed( vk::Format format ); Obtiene el número de bits en los que se empaqueta el formato. Un solo elemento de imagen en este formato se puede almacenar en el mismo espacio que un tipo escalar de este ancho de bits.uint8_t componentCount( vk::Format format ); Obtiene el número de componentes de este formato.bool componentsAreCompressed( vk::Format format ); Verdadero, si los componentes de este formato están comprimidos, de lo contrario falsos.uint8_t componentBits( vk::Format format, uint8_t component ); Obtiene el número de bits en este componente, si no se comprime, de lo contrario 0.char const * componentName( vk::Format format, uint8_t component ); Obtiene el nombre de este componente como una cadena C.char const * componentNumericFormat( vk::Format format, uint8_t component ); Obtiene el formato numérico de este componente como una cadena C.uint8_t componentPlaneIndex( vk::Format format, uint8_t component ); Obtiene el índice de plano, este componente se encuentra.uint8_t planeCount( vk::Format format ); Obtiene el número de planos de imagen de este formato.vk::Format planeCompatibleFormat( vk::Format format, uint8_t plane ); Obtiene un formato de un solo plano compatible con este avión.uint8_t planeHeightDivisor( vk::Format format, uint8_t plane ); Obtiene la altura relativa de este avión. Un valor de k significa que este avión es 1/k la altura del formato general.uint8_t planeWidthDivisor( vk::Format format, uint8_t plane ); Obtiene el ancho relativo de este avión. Un valor de k significa que este avión es 1/k el ancho del formato general. Con el encabezado adicional vulkan_hash.hpp , obtienes especializaciones de std::hash para las clases de envoltorio de identificación y, con C ++ 14, para los envoltorios de estructura. Con VULKAN_HPP_HASH_COMBINE , puede definir su propio algoritmo de combinación de hash para los elementos de estructura.
Con el encabezado adicional vulkan_extension_inspection.hpp , se proporcionan algunas funciones para inspeccionar extensiones. Con C ++ 20 y más, algunas de esas funciones están marcadas como constexpr , es decir, con argumentos apropiados, se resuelven en el momento de la compilación. Cada extensión se identifica mediante una cadena que contiene su nombre. Tenga en cuenta que existe una definición con ese nombre para cada extensión. Algunas funciones pueden proporcionar información que depende de la versión Vulkan. Como todas las funciones aquí funcionan únicamente en cadenas, las versiones Vulkan están codificadas por una cadena que comienza con "VK_Version_", seguida de la versión mayor y menor, separadas por una correa baja, como esta: "VK_VERSION_1_0".
std::set<std::string> const & getDeviceExtensions(); Obtiene todas las extensiones de dispositivo especificadas para la plataforma actual. Tenga en cuenta que no todos pueden ser compatibles con los dispositivos reales.std::set<std::string> const & getInstanceExtensions(); Obtiene todas las extensiones de instancias especificadas para la plataforma actual. Tenga en cuenta que no todos podrían estar respaldados por las instancias reales.std::map<std::string, std::string> const & getDeprecatedExtensions(); Obtiene un mapa de todas las extensiones desapercibidas a la extensión o la versión Vulkan que se supone que reemplaza esa funcionalidad.std::map<std::string, std::vector<std::vector<std::string>>> const & getExtensionDepends( std::string const & extension ); Algunas extensiones dependen de otras extensiones. Esas dependencias pueden diferir para diferentes versiones de Vulkan, y puede haber diferentes conjuntos de dependencias para la misma versión de Vulkan. Esta función obtiene un vector de vectores de extensiones por versión Vulkan de la que depende la extensión dada.std::pair<bool, std::vector<std::vector<std::string>> const &> getExtensionDepends( std::string const & version, std::string const & extension ); El first miembro del std::pair devuelto es verdadero, si la extensión dada se especifica para la versión vulkan dada, de lo contrario false . El second miembro del std::pair devuelto es un vector de vectores de extensiones, enumerando los conjuntos separados de extensiones de la extensión dada depende de la versión vulkan dada.std::map<std::string, std::string> const & getObsoletedExtensions(); Obtiene un mapa de todas las extensiones obsoletas a la extensión o la versión Vulkan que ha obsoletado esa extensión.std::map<std::string, std::string> const & getPromotedExtensions(); Obtiene un mapa de todas las extensiones que se promovieron a otra extensión o a una versión de Vulkan a esa extensión de la versión Vulkan.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionDeprecatedBy( std::string const & extension ); Obtiene la extensión o la versión Vulkan, la extensión dada está desaprobada.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionObsoletedBy( std::string const & extension ); Obtiene la extensión o la versión Vulkan, la extensión dada está obsoleta.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionPromotedTo( std::string const & extension ); Obtiene la extensión o la versión Vulkan a la que se promueve la extensión dada.VULKAN_HPP_CONSTEXPR_20 bool isDeprecatedExtension( std::string const & extension ); Devuelve true si la extensión dada está desaprobada por alguna otra extensión o versión de Vulkan.VULKAN_HPP_CONSTEXPR_20 bool isDeviceExtension( std::string const & extension ); Devuelve true si la extensión dada es una extensión del dispositivo.VULKAN_HPP_CONSTEXPR_20 bool isInstanceExtension( std::string const & extension ); Devuelve true si la extensión dada es una extensión de instancia.VULKAN_HPP_CONSTEXPR_20 bool isObsoletedExtension( std::string const & extension ); Devuelve true si la extensión dada está obsoleta por alguna otra extensión o versión Vulkan.VULKAN_HPP_CONSTEXPR_20 bool isPromotedExtension( std::string const & extension ); Devuelve true si la extensión dada se promueve a alguna otra extensión o versión Vulkan.Advertencia
La versión actual de Microsoft Visual Studio 2022 no puede manejar el módulo vulkan.cppm. Se archiva un error (https://developerpersommunity.visualstudio.com/t/on-building-a-c20-module:-fatal--error/10469799#t-nd10485943). Al menos puede usar esta función si no necesita o desea usar vk::UniqueHandle o vk::SharedHandle definiendo VULKAN_HPP_NO_SMART_HANDLE .
Vulkan-HPP proporciona un módulo llamado C ++, vulkan_hpp en vulkan.cppm . Los módulos C ++ están destinados a reemplazar los archivos de encabezado. Los módulos tienen potencial para mejorar drásticamente los tiempos de compilación para grandes proyectos, ya que las declaraciones y definiciones pueden compartirse fácilmente entre las unidades de traducción sin analizar repetidamente los encabezados. Vulkan-HPP tiene algunos encabezados extremadamente largos (por ejemplo, vulkan_structs.hpp ), y es probable que el módulo C ++ acorte los tiempos de compilación para proyectos que actualmente lo usan.
Esta característica requiere un compilador reciente con soporte completo de C ++ 20:
cl.exe 19.28 o posterior)Si tiene la intención de usar el soporte del módulo C ++ de CMake (y posiblemente ninja), se requieren herramientas más recientes:
cl.exe 19.34 o posterior)Advertencia
El módulo llamado Vulkan-HPP C ++ sigue siendo experimental. Algunas formas sugeridas de usarlo en sus proyectos están a continuación. 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 . Por ejemplo:
# 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.
Licenciado bajo la licencia Apache, versión 2.0 (la "licencia"); No puede usar este archivo, excepto de conformidad con la licencia. Puede obtener una copia de la licencia en
http://www.apache.org/licenses/LICENSE-2.0
A menos que la ley aplicable sea requerida o acordado por escrito, el software distribuido bajo la licencia se distribuye de manera "como es", sin garantías o condiciones de ningún tipo, ya sea expresas o implícitas. Consulte la licencia para los permisos y limitaciones de rigor de idioma específico bajo la licencia.