Tujuan dari Vulkan-HPP adalah untuk memberikan binding C ++ hanya header untuk API V Vulkan untuk meningkatkan pengalaman pengembang Vulkan tanpa memperkenalkan biaya runtime CPU. Ini menambahkan fitur seperti jenis keamanan untuk enum dan bitfields, dukungan wadah STL, pengecualian dan enumerasi sederhana.
| Platform | Bangun status |
|---|---|
| Linux |
Vulkan-HPP adalah bagian dari Lunarg Vulkan SDK sejak versi 1.0.24. Just #include <vulkan/vulkan.hpp> dan Anda siap menggunakan binding C ++. Jika Anda menggunakan versi Vulkan yang belum didukung oleh Vulkan SDK, Anda dapat menemukan versi terbaru dari header di sini.
Vulkan-HPP membutuhkan kompiler C ++ 11 untuk dikompilasi. Kompiler berikut diketahui berfungsi:
Untuk membangun sampel dan tes lokal, Anda harus mengkloning repositori ini dan menjalankan cmake untuk menghasilkan file build yang diperlukan
Instal dependensi.
Buka shell yang memberikan git dan klon repositori dengan:
git clone --recurse-submodules https://github.com/KhronosGroup/Vulkan-Hpp.git
Ubah direktori saat ini ke direktori Vulkan-HPP yang baru dibuat.
Buat lingkungan build dengan 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
Anda mungkin perlu menentukan generator melalui -G , untuk daftar lengkap generator yang menjalankan cmake -G .
vulkan.hpp dari file registri vk.xml xml, tambahkan -DVULKAN_HPP_RUN_GENERATOR=ON opsi ke baris perintah cmake. Buka proyek yang dihasilkan dengan IDE, misalnya Visual Studio atau luncurkan proses pembuatan dengan cmake --build build --parallel .
Opsional: Untuk memperbarui vulkan-HPP dan submodulnya mengeksekusi git pull --recurse-submodules .
Anda dapat mengunduh dan menginstal vulkan-hpp menggunakan VCPKG Dependency Manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install vulkan-headersPort Vulkan-HPP di VCPKG terus diperbarui oleh anggota tim Microsoft dan kontributor komunitas. Jika versi sudah ketinggalan zaman, silakan buat masalah atau tarik permintaan pada repositori VCPKG.
Jika program clang-format ditemukan oleh CMake, define CLANG_FORMAT_EXECUTABLE diatur sesuai. Dalam hal ini, vulkan.hpp yang dihasilkan diformat menggunakan file .clang-format yang terletak di direktori root proyek ini. Kalau tidak, itu diformat sebagai kode keras di generator.
File VulkanHpp.natvis memberikan tampilan khusus di vk::Flags untuk Visual Studio. Jika Anda menambahkan file ini ke direktori NATVIS khusus pengguna dari instalasi Visual Studio Anda (%userprofile% Documents Visual Studio 2022 Visualizers), Anda mendapatkan vk::Flags dengan baik diformat dengan baik di debugger Anda dengan semua proyek studio visual Anda.
Untuk menghindari tabrakan nama dengan API vol C, ikatan C ++ berada di namespace vk . Aturan berikut berlaku untuk penamaan baru:
Vk . Selain itu, huruf fungsi pertama adalah huruf kecil.vkCreateInstance dapat diakses sebagai vk::createInstance .VkImageTiling dapat diakses sebagai vk::ImageTiling .VkImageCreateInfo dapat diakses sebagai vk::ImageCreateInfo .VK_ dan ketik infix dihapus. Jika jenis enum adalah ekstensi, akhiran ekstensi telah dihapus dari nilai enum.Dalam semua kasus lain akhiran ekstensi belum dihapus.
VK_IMAGETYPE_2D sekarang vk::ImageType::e2D .VK_COLOR_SPACE_SRGB_NONLINEAR_KHR sekarang vk::ColorSpaceKHR::eSrgbNonlinear .VK_STRUCTURE_TYPE_PRESENT_INFO_KHR sekarang vk::StructureType::ePresentInfoKHR ._BIT juga telah dihapus. Dalam beberapa kasus mungkin perlu untuk memindahkan vulkan-hpp ke namespace khusus. Ini dapat dicapai dengan mendefinisikan VULKAN_HPP_NAMESPACE sebelum memasukkan vulkan-hpp.
Vulkan-HPP mendeklarasikan kelas untuk semua pegangan untuk memastikan keamanan tipe penuh dan untuk menambahkan dukungan untuk fungsi anggota pada pegangan. Fungsi anggota telah ditambahkan ke kelas pegangan untuk setiap fungsi yang menerima pegangan yang sesuai sebagai parameter pertama. Alih -alih vkBindBufferMemory(device, ...) seseorang dapat menulis device.bindBufferMemory(...) atau vk::bindBufferMemory(device, ...) .
Ada header tambahan bernama vulkan_raii.hpp yang dihasilkan. Header itu memegang kelas pembungkus yang sesuai dengan RAII untuk jenis pegangan. Artinya, misalnya tipe pegangan VkInstance , ada pembungkus yang sesuai dengan vk::raii::Instance . Harap lihat sampel menggunakan kelas -kelas tersebut di direktori raii_samples.
Pada platform 64-bit, Vulkan-HPP mendukung konversi implisit antara pegangan C ++ vulkan dan pegangan C vulkan. Pada platform 32-bit, semua pegangan yang tidak dapat diselenggarakan didefinisikan sebagai uint64_t , sehingga mencegah pemeriksaan konversi tipe pada waktu kompilasi yang akan menangkap tugas antara jenis pegangan yang tidak kompatibel. Karena itu vulkan-HPP tidak memungkinkan konversi implisit untuk platform 32-bit secara default dan disarankan untuk menggunakan static_cast untuk konversi seperti ini: VkImage = static_cast<VkImage>(cppImage) untuk mencegah konversi beberapa int sewenang-wenang ke pegangan atau sebaliknya secara tidak sengaja. Jika Anda mengembangkan kode Anda pada platform 64-bit, tetapi ingin mengkompilasi kode Anda untuk platform 32-bit tanpa menambahkan gips eksplisit, Anda dapat mendefinisikan VULKAN_HPP_TYPESAFE_CONVERSION ke 1 dalam sistem build Anda atau sebelum memasukkan vulkan.hpp . Pada platform 64-bit yang ditentukan ini diatur ke 1 secara default dan dapat diatur ke 0 untuk menonaktifkan konversi implisit.
Fitur Enum Scoped menambahkan jenis keamanan ke bendera, tetapi juga mencegah menggunakan bit flag sebagai input untuk operasi bitwise seperti & dan | .
Sebagai solusi vulkan-hpp menyediakan template kelas vk::Flags yang membawa operasi standar seperti &= , |= , & , dan | ke enum kami yang terluka. Kecuali untuk inisialisasi dengan 0 kelas ini berperilaku persis seperti bitma normal dengan peningkatan bahwa tidak mungkin untuk mengatur bit yang tidak ditentukan oleh enum yang sesuai secara tidak sengaja. Berikut adalah beberapa contoh untuk penanganan 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 */ , ...); Saat membangun pegangan di vulkan, seseorang biasanya harus membuat beberapa struct CreateInfo yang menggambarkan pegangan baru. Ini dapat menghasilkan kode yang cukup panjang seperti yang dapat dilihat dalam contoh volkan C berikut:
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); Ada dua masalah khas yang dihadapi Vulkan Developers saat mengisi bidang CreateInfo struct berdasarkan bidang:
sType salah.Terutama yang pertama sulit dideteksi.
Vulkan-HPP menyediakan konstruktor untuk semua objek CreateInfo yang menerima satu parameter untuk setiap variabel anggota. Dengan cara ini kompiler melempar kesalahan kompiler jika nilai telah dilupakan. Selain sType ini secara otomatis diisi dengan nilai yang benar dan pNext diatur ke nullptr secara default. Beginilah kode yang sama terlihat dengan konstruktor:
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); Dengan konstruktor untuk struktur CreateInfo , orang juga dapat meneruskan temporari ke fungsi vulkan seperti ini:
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}); Dimulai dengan C ++ 20, C ++ mendukung inisialisasi yang ditunjuk. Karena fitur itu perlu tidak memiliki konstruktor yang ditentukan pengguna atau warisan, Anda harus #define VULKAN_HPP_NO_CONSTRUCTORS , yang menghilangkan semua konstruktor struktur dan serikat dari vulkan.hpp . Sebaliknya Anda dapat menggunakan inisialisasi agregat. Beberapa garis VK pertama di sumber Anda mungkin terlihat seperti:
// 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 };alih-alih
// 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); Perhatikan, bahwa pesanan penunjuk perlu cocok dengan pesanan deklarasi. Catatan juga, bahwa sekarang Anda dapat secara eksplisit mengatur anggota sType Struktur VK. Ini tidak perlu (karena mereka diinisialisasi dengan benar secara default) atau direkomendasikan.
API Vulkan memiliki beberapa tempat yang membutuhkan (menghitung, penunjuk) sebagai dua argumen fungsi dan C ++ memiliki beberapa wadah yang memetakan dengan sempurna untuk pasangan ini. Untuk menyederhanakan pengembangan, binding vulkan-HPP telah menggantikan pasangan argumen tersebut dengan vk::ArrayProxy class template yang menerima array kosong dan nilai tunggal serta wadah STL std::initializer_list , std::array dan std::vector sebagai argumen untuk konstruksi. Dengan cara ini, versi Vulkan yang dihasilkan tunggal dapat menerima berbagai input tanpa memiliki ledakan kombinatorik yang akan terjadi saat membuat fungsi untuk setiap jenis wadah.
Berikut adalah beberapa sampel kode tentang cara menggunakan 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 menghasilkan referensi untuk pointer ke struct. Konversi ini memungkinkan lulus struct sementara ke fungsi yang dapat menghasilkan kode yang lebih pendek. Dalam hal input adalah opsional dan dengan demikian menerima penunjuk nol, tipe parameter akan menjadi vk::Optional<T> const& . Jenis ini menerima referensi ke T atau nullptr sebagai input dan dengan demikian memungkinkan struct sementara opsional.
// 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 memungkinkan rantai struktur melalui pointer pNext . Vulkan-HPP memiliki template kelas variadik yang memungkinkan pembangunan rantai struktur tersebut dengan upaya minimal. Selain itu ia memeriksa pada waktu kompilasi jika spek memungkinkan konstruksi rantai pNext tersebut.
// 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 menyediakan konstruktor untuk rantai ini yang mirip dengan objek CreateInfo yang menerima daftar semua struktur bagian dari rantai. Bidang pNext secara otomatis diatur ke nilai yang benar:
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> c = {
vk::MemoryAllocateInfo (size, type),
vk::MemoryDedicatedAllocateInfo (image)
}; Jika salah satu struktur struktureChain harus dihapus, mungkin karena beberapa pengaturan opsional, Anda dapat menggunakan fungsi vk::StructureChain::unlink<ClassType>() . Ini memodifikasi strukturechain sedemikian rupa sehingga struktur yang ditentukan bukan bagian dari rantai pnext lagi. Perhatikan, bahwa tata letak memori aktual dari struktur tidak dimodifikasi oleh fungsi itu. Jika struktur yang sama harus ditambahkan kembali ke struktureChain lagi, gunakan vk::StructureChain::relink<ClassType>() .
Terkadang pengguna harus melewati rantai struktur yang telah ditentukan sebelumnya untuk meminta informasi. Untuk kasus -kasus tersebut ada dua fungsi pengambil yang sesuai. Satu dengan templat variadik yang menghasilkan rantai struktur setidaknya dua elemen untuk membangun nilai pengembalian:
// 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>();Untuk mendapatkan struktur dasar, tanpa rantai, fungsi pengambil lainnya yang diberikan tidak memerlukan argumen template untuk mendapatkan:
// Query just vk::MemoryRequirements2KHR
vk::MemoryRequirements2KHR memoryRequirements = device.getBufferMemoryRequirements2KHR({}); Secara default vulkan-hpp memiliki pengecualian yang diaktifkan. Ini berarti bahwa Vulkan-HPP memeriksa kode pengembalian dari setiap panggilan fungsi yang mengembalikan vk::Result . Jika vk::Result adalah kegagalan A std::runtime_error akan dilemparkan. Karena tidak perlu mengembalikan kode kesalahan lagi, binding C ++ sekarang dapat mengembalikan nilai pengembalian yang diinginkan aktual, yaitu pegangan vulkan. Dalam kasus -kasus tersebut vk::ResultValue<SomeType>::type didefinisikan sebagai tipe yang dikembalikan.
Untuk membuat perangkat, Anda sekarang dapat menulis:
vk::Device device = physicalDevice.createDevice(createInfo); Beberapa fungsi memungkinkan lebih dari sekedar vk::Result::eSuccess dianggap sebagai kode keberhasilan. Untuk fungsi -fungsi itu, kami selalu mengembalikan vk::ResultValue<SomeType> . Contohnya adalah acquireNextImage2KHR , yang dapat digunakan seperti ini:
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 ;
} Seiring berjalannya waktu, beberapa fungsi Vulkan mungkin berubah, sehingga mereka mulai mendukung lebih banyak kode hasil daripada vk::Result::eSuccess sebagai kode keberhasilan. Perubahan logis itu tidak akan terlihat di C API, tetapi di C ++ API, karena fungsi seperti itu sekarang akan mengembalikan vk::ResultValue<SomeType> bukan hanya SomeType . Dalam kasus (jarang) seperti itu, Anda harus menyesuaikan sumber CPP Anda untuk mencerminkan perubahan API itu.
Jika penanganan pengecualian dinonaktifkan dengan mendefinisikan VULKAN_HPP_NO_EXCEPTIONS jenis vk::ResultValue<SomeType>::type adalah struct yang memegang vk::Result dan SomeType . Struct ini mendukung membongkar nilai pengembalian dengan menggunakan std::tie .
Jika Anda tidak ingin menggunakan vk::ArrayProxy dan Transformasi Nilai Pengembalian, Anda masih dapat memanggil fungsi gaya-C biasa. Di bawah ini adalah tiga contoh yang menunjukkan 3 cara untuk menggunakan API:
Cuplikan pertama menunjukkan cara menggunakan API tanpa pengecualian dan transformasi nilai pengembalian:
// 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 ?
}Cuplikan kedua menunjukkan cara menggunakan API menggunakan transformasi nilai pengembalian, tetapi tanpa kecuali. Ini sudah sedikit lebih pendek dari kode asli:
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 ?
}Cara yang lebih baik untuk membongkar hasilnya adalah menggunakan binding terstruktur di C ++ 17. Mereka akan memungkinkan kita untuk mendapatkan hasilnya dengan satu baris kode:
auto [result, shaderModule2] = device.createShaderModule({...} /* createInfo temporary */ );Akhirnya, contoh kode terakhir adalah menggunakan pengecualian dan transformasi nilai pengembalian. Ini adalah mode default API.
vk::ShaderModule shader1;
vk::ShaderModule shader2;
try
{
shader1 = device. createShaderModule ({...});
shader2 = device. createShaderModule ({...});
}
catch (std:: exception const &e)
{
// handle error and free resources
}Penting
The Vulkan menangani di vk -namespace tidak mendukung RAII, maka Anda perlu membersihkan sumber daya Anda di pawang kesalahan! Sebaliknya, Anda bisa menggunakan kelas pembungkus pegangan di vk::raii -namespace.
Dengan C ++ 17 ke atas, beberapa fungsi dikaitkan dengan [[nodiscard]] , menghasilkan peringatan jika Anda tidak menggunakan nilai pengembalian dengan cara apa pun. Anda dapat mematikan peringatan itu dengan mendefinisikan VULKAN_HPP_NO_NODISCARD_WARNINGS .
Untuk transformasi nilai pengembalian, ada satu kelas khusus nilai pengembalian yang memerlukan penanganan khusus: enumerasi. Untuk enumerasi Anda biasanya harus menulis kode seperti ini:
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);Karena menulis loop ini berulang kali membosankan dan rentan kesalahan, ikatan C ++ mengurus enumerasi sehingga Anda bisa menulis:
std::vector<LayerProperties> properties = physicalDevice.enumerateDeviceLayerProperties(); Vulkan-HPP menyediakan antarmuka vk::UniqueHandle<Type, Deleter> . Untuk setiap vulkan handle type vk::Type ada pegangan unik vk::UniqueType yang akan menghapus sumber daya vulkan yang mendasarinya saat kehancuran, misalnya vk::UniqueBuffer adalah pegangan unik untuk vk::Buffer .
Untuk setiap fungsi yang membangun pegangan vulkan dari tipe vk::Type Vulkan-HPP menyediakan versi kedua yang mengembalikan vk::UniqueType . Misalnya untuk vk::Device::createBuffer ada vk::Device::createBufferUnique dan untuk vk::allocateCommandBuffers ada vk::allocateCommandBuffersUnique .
Perhatikan bahwa menggunakan vk::UniqueHandle datang dengan biaya karena sebagian besar penghapus harus menyimpan vk::AllocationCallbacks dan Parent Handle yang digunakan untuk konstruksi karena diperlukan untuk penghancuran otomatis.
Vulkan-HPP menyediakan antarmuka vk::SharedHandle<Type> . Untuk setiap vulkan handle type vk::Type ada pegangan bersama vk::SharedType yang akan menghapus sumber daya vulkan yang mendasarinya saat kehancuran, misalnya vk::SharedBuffer adalah pegangan bersama untuk vk::Buffer .
Tidak seperti vk::UniqueHandle , vk::SharedHandle mengambil kepemilikan bersama atas sumber daya serta orang tuanya. Ini berarti bahwa pegangan induk tidak akan dihancurkan sampai semua sumber daya anak dihapus. Ini berguna untuk sumber daya yang dibagi antara beberapa utas atau objek.
Mekanisme ini memastikan urutan kerusakan yang benar bahkan jika induk vk::SharedHandle dihancurkan sebelum menangani anaknya. Kalau tidak, pegangan berperilaku seperti std::shared_ptr . vk::SharedInstance atau salah satu objek anaknya harus menjadi yang terakhir dihapus (pertama dibuat, pertama dalam deklarasi kelas).
Belum ada fungsi yang mengembalikan vk::SharedHandle secara langsung. Sebaliknya, Anda dapat membangun vk::SharedHandle dari vk::Handle :
vk::Buffer buffer = device.createBuffer(...);
vk::SharedBuffer sharedBuffer (buffer, device); // sharedBuffer now owns the buffer Ada beberapa spesialisasi vk::SharedHandle untuk jenis pegangan yang berbeda. Misalnya, vk::SharedImage dapat mengambil argumen tambahan untuk menentukan apakah gambar dimiliki oleh 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 Ada juga spesialisasi untuk vk::SwapchainKHR yang mengambil argumen tambahan untuk menentukan permukaan:
vk::SwapchainKHR swapchain = device.createSwapchainKHR(...);
vk::SharedSwapchainKHR sharedSwapchain (swapchain, device, surface); // sharedSwapchain now owns the swapchain and surface Anda dapat membuat vk::SharedHandle Overload untuk tipe pegangan Anda sendiri atau pegangan bersama dengan memberikan beberapa argumen template ke SharedHandleBase :
Dengan ini, berikan fungsi penghancuran statis kustom internalDestroy , yang mengambil pegangan induk dan pegangan untuk dihancurkan. Jangan lupa untuk menambahkan deklarasi teman untuk kelas dasar.
// 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);
}
};API akan diperluas untuk menyediakan fungsi penciptaan di masa depan.
Selain vk::UniqueHandles dan vk::SharedHandles , ada satu set kelas pembungkus untuk semua jenis pegangan yang mengikuti RAII-paradigma (akuisisi sumber daya adalah inisialisasi), disediakan dalam vk::raii namespace.
Sementara vk::UniqueHandle meniru pegangan yang dibungkus dengan std::unique_ptr , dan vk::SharedHandle meniru pegangan yang dibungkus oleh std::shared_ptr , termasuk informasi induk, vk::raii::Handle hanyalah kelas yang memperoleh vk-handle yang meremehkan. Dengan demikian, Anda bebas menggunakannya sebagai nilai atau membungkusnya dengan beberapa penunjuk pintar.
Selain vk::Handles , semua kelas pembungkus menangani perlu menyimpan data tambahan, dan karenanya tidak identik dengan biner dengan candle c vol.
Seperti vk::UniqueHandles dan vk::SharedHandles menggunakan operator yang sama dengan vk::Handles , mereka dapat dengan mudah dicampur dan cocok. The vk::raii::Handles Gunakan beberapa dispatcher yang sedikit berbeda dan karenanya tidak kompatibel dengan pegangan lainnya! Yaitu, untuk vk-Handles , vk::UniqueHandles , dan vk::SharedHandles , Anda perlu membuat instantiate seorang pengirim global seperti yang dijelaskan dalam https://github.com/khronosgroup/vulkan-hpp#extensions-per-function-function-pointers. Untuk vk::raii-Handles , ini tidak diperlukan, karena mereka memelihara operator mereka sendiri. Keuntungan besar di sini adalah ketika Anda memiliki beberapa perangkat: fungsi yang disebut melalui vk::raii-Handles selalu memanggil fungsi perangkat spesifik.
Terkadang diperlukan untuk menggunakan std::vector dengan alokasi khusus. Vulkan-HPP mendukung vektor dengan alokasi khusus sebagai input untuk vk::ArrayProxy dan untuk fungsi yang memang mengembalikan vektor. Untuk kasus terakhir, tambahkan alokasi kustom favorit Anda sebagai argumen template ke panggilan fungsi seperti ini:
std::vector<LayerProperties, MyCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties<MyCustomAllocator>(); Anda juga dapat menggunakan pengalokasi kustom yang stateful dengan memberikannya sebagai argumen untuk fungsi -fungsi tersebut. Sayangnya, untuk membuat kompiler senang, Anda juga perlu secara eksplisit mengatur argumen pengiriman. Untuk mendapatkan default di sana, {} sederhana sudah cukup:
MyStatefulCustomAllocator allocator;
std::vector<LayerProperties, MyStatefulCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties(allocator, {}); Di seluruh vulkan.hpp , ada beberapa panggilan ke fungsi menegaskan. Dengan mendefinisikan VULKAN_HPP_ASSERT , Anda dapat menentukan fungsi penegasan khusus Anda sendiri untuk dipanggil.
Secara default, VULKAN_HPP_ASSERT_ON_RESULT akan digunakan untuk memeriksa hasil ketika VULKAN_HPP_NO_EXCEPTIONS didefinisikan. Jika Anda ingin menangani kesalahan sendiri, Anda dapat menonaktifkan/menyesuaikannya seperti VULKAN_HPP_ASSERT .
Ada beberapa pernyataan statis untuk setiap kelas pegangan dan setiap struct dalam file vulkan_static_assertions.hpp . Anda mungkin memasukkan file itu di setidaknya salah satu file sumber Anda. Dengan mendefinisikan VULKAN_HPP_STATIC_ASSERT , Anda dapat menentukan pernyataan statis kustom Anda sendiri untuk digunakan untuk kasus -kasus tersebut. Artinya, dengan mendefinisikannya sebagai NOP, Anda dapat sedikit mengurangi waktu kompilasi Anda.
Vulkan Loader hanya memaparkan fungsi inti Vulkan dan sejumlah ekstensi. Untuk menggunakan vulkan-HPP dengan ekstensi yang diperlukan untuk memiliki perpustakaan yang menyediakan stub untuk semua fungsi vulkan yang digunakan atau untuk memberitahu Vulkan-HPP untuk mengirimkan pointer fungsi tersebut. Vulkan-HPP menyediakan mekanisme pengiriman per fungsi dengan menerima kelas pengiriman sebagai parameter terakhir dalam setiap panggilan fungsi. Kelas pengiriman harus memberikan jenis yang dapat dipanggil untuk setiap fungsi Vulkan yang digunakan. Vulkan-HPP menyediakan satu implementasi, DispatchLoaderDynamic , yang mengambil semua pointer fungsi yang diketahui oleh perpustakaan.
// 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); Untuk menggunakan vk::DispatchLoaderDynamic sebagai Dispatcher default (berarti: Anda tidak perlu secara eksplisit menambahkannya ke setiap panggilan fungsi), Anda perlu #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 , dan mintalah macro VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE for that countyerererer_dynamic. Kemudian Anda dapat menggunakannya dengan makro VULKAN_HPP_DEFAULT_DISPATCHER , seperti yang ditunjukkan pada cuplikan kode di bawah ini. Membuat vk::DispatchLoaderDynamic fitur lengkap adalah proses dua hingga tiga langkah, di mana Anda memiliki tiga pilihan untuk langkah pertama:
vk::DynamicLoader : VULKAN_HPP_DEFAULT_DISPATCHER.init();getProcAddress (bandingkan dengan vk::DynamicLoader di vulkan.hpp ): YourDynamicLoader ydl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(ydl);Catatan
Anda perlu menjaga objek Loader dinamis tetap hidup sampai setelah panggilan terakhir ke fungsi Vulkan dalam program Anda. Misalnya dengan membuatnya statis, atau menyimpannya secara global.
PFN_vkGetInstanceProcAddr : PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = your_own_function_pointer_getter();
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);vk::Instance untuk mendapatkan semua pointer fungsi lainnya: vk::Instance instance = vk::createInstance({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);vk::Device untuk mendapatkan pointer fungsi khusus perangkat std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
assert (!physicalDevices.empty());
vk::Device device = physicalDevices[ 0 ].createDevice({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(device);Setelah langkah kedua di atas, operator berfungsi penuh. Menambahkan langkah ketiga berpotensi menghasilkan kode yang lebih efisien. Tetapi jika Anda bermaksud menggunakan beberapa perangkat, Anda bisa menghilangkan langkah ketiga itu dan membiarkan driver melakukan penyiaran perangkat.
Dalam beberapa kasus, penyimpanan untuk Dispatchloaderdynamic harus tertanam dalam DLL. Untuk kasus-kasus itu, Anda perlu mendefinisikan VULKAN_HPP_STORAGE_SHARED untuk memberi tahu Vulkan-HPP bahwa penyimpanan berada di DLL. Saat mengkompilasi DLL dengan penyimpanan, ia juga diperlukan untuk mendefinisikan VULKAN_HPP_STORAGE_SHARED_EXPORT untuk mengekspor simbol yang diperlukan.
Untuk semua fungsi, VULKAN_HPP_DEFAULT_DISPATCHER adalah default untuk argumen terakhir untuk fungsi itu. Jika Anda ingin secara eksplisit menyediakan operator untuk setiap panggilan fungsi (ketika Anda memiliki banyak operator untuk perangkat yang berbeda, misalnya) dan Anda ingin memastikan, bahwa Anda tidak secara tidak sengaja melewatkan panggilan fungsi apa pun, Anda dapat mendefinisikan VULKAN_HPP_NO_DEFAULT_DISPATCHER sebelum Anda memasukkan vulkan.hpp untuk menghapus argumen default itu.
vulkan.hpp menyediakan beberapa jenis sifat, memudahkan Metaprogramming:
template <typename EnumType, EnumType value> struct CppType memetakan nilai IndexType ( IndexType::eUint16 , IndexType::eUint32 , ...) ke tipe yang sesuai ( uint16_t , uint32_t , ...) oleh Type tipe anggota; Peta nilai ObjectType ( ObjectType::eInstance , ObjectType::eDevice , ...) ke tipe yang sesuai ( vk::Instance , vk::Device , ...) dengan Type tipe anggota; Peta DebugReportObjectType Values ( DebugReportObjectTypeEXT::eInstance , DebugReportObjectTypeEXT::eDevice , ...) ke tipe yang sesuai ( vk::Instance , vk::Device , ...) oleh Type jenis anggota;template <typename T> struct IndexTypeValue memetakan jenis skalar ( uint16_t , uint32_t , ...) ke nilai IndexType yang sesuai ( IndexType::eUint16 , IndexType::eUint32 , ...).template <typename T> struct isVulkanHandleType memetakan tipe ke true jika dan hanya jika itu adalah kelas pegangan ( vk::Instance , vk::Device , ...) dengan value anggota statis.HandleClass::CType memetakan kelas pegangan ( vk::Instance , vk::Device , ...) ke tipe-C yang sesuai ( VkInstance , VkDevice , ...) oleh tipe anggota CType .HandleClass::objectType memetakan kelas pegangan ( vk::Instance , vk::Device , ...) ke nilai ObjectType yang sesuai ( ObjectType::eInstance , ObjectType::eDevice , ...) oleh anggota statis objectType .HandleClass::debugReportObjectType memetakan kelas pegangan ( vk::Instance , vk::Device , ...) ke nilai DebugReportObjectTypeEXT ( DebugReportObjectTypeEXT::eInstance , debugReportObjectTypeext :: edevice, ...) oleh The Static, DebugReportOpexext :: Edevice, ...) oleh The Static, The Static, DebugREPORTTYPEEXE DebugReportObjectTypeEXT::eDevice , ...) oleh The Static, The Static, debugReportObjectType . Dengan header tambahan vulkan_format_traits.hpp , beberapa fungsi sifat pada vk::Format disediakan. Dengan C ++ 14 ke atas, semua fungsi tersebut ditandai sebagai constexpr , yaitu dengan argumen yang sesuai, mereka diselesaikan pada waktu kompilasi.
uin8_t blockSize( vk::Format format ); Mendapat ukuran blok Texel dari format ini dalam byte.uint8_t texelsPerBlock( vk::Format format ); Mendapat jumlah Texel di blok Texel.std::array<uint8_t, 3> blockExtent( vk::Format format ); Mendapat tingkat tiga dimensi blok Texel.char const * compressionScheme( vk::Format format ); Mendapat deskripsi tekstual tentang skema kompresi dari format ini, atau teks kosong jika tidak dikompresi.bool isCompressed( vk::Format format ); Benar, jika format adalah format terkompresi, jika tidak salah.uint8_t packed( vk::Format format ); Mendapat jumlah bit di mana format dikemas. Elemen gambar tunggal dalam format ini dapat disimpan dalam ruang yang sama dengan jenis skalar dari lebar bit ini.uint8_t componentCount( vk::Format format ); Mendapat jumlah komponen dari format ini.bool componentsAreCompressed( vk::Format format ); Benar, jika komponen format ini dikompresi, jika tidak salah.uint8_t componentBits( vk::Format format, uint8_t component ); Mendapat jumlah bit dalam komponen ini, jika tidak dikompresi, jika tidak 0.char const * componentName( vk::Format format, uint8_t component ); Mendapat nama komponen ini sebagai c-string.char const * componentNumericFormat( vk::Format format, uint8_t component ); Mendapat format numerik dari komponen ini sebagai c-string.uint8_t componentPlaneIndex( vk::Format format, uint8_t component ); Mendapat indeks pesawat, komponen ini terletak.uint8_t planeCount( vk::Format format ); Mendapat jumlah bidang gambar dari format ini.vk::Format planeCompatibleFormat( vk::Format format, uint8_t plane ); Mendapat format pesawat tunggal yang kompatibel dengan bidang ini.uint8_t planeHeightDivisor( vk::Format format, uint8_t plane ); Mendapat ketinggian relatif pesawat ini. Nilai k berarti bahwa bidang ini adalah 1/k tinggi dari keseluruhan format.uint8_t planeWidthDivisor( vk::Format format, uint8_t plane ); Mendapat lebar relatif dari pesawat ini. Nilai k berarti bahwa bidang ini adalah 1/k lebar format keseluruhan. Dengan header tambahan vulkan_hash.hpp , Anda mendapatkan spesialisasi std::hash untuk kelas pembungkus pegangan dan, dengan C ++ 14, untuk pembungkus struktur. Dengan VULKAN_HPP_HASH_COMBINE , Anda dapat mendefinisikan hash Anda sendiri menggabungkan algoritma untuk elemen struktur.
Dengan header tambahan vulkan_extension_inspection.hpp , beberapa fungsi untuk memeriksa ekstensi disediakan. Dengan C ++ 20 ke atas, beberapa fungsi tersebut ditandai sebagai constexpr , yaitu dengan argumen yang sesuai, mereka diselesaikan pada waktu kompilasi. Setiap ekstensi diidentifikasi oleh string yang memegang namanya. Perhatikan bahwa ada definisi dengan nama itu untuk setiap ekstensi. Beberapa fungsi mungkin memberikan informasi yang tergantung pada versi Vulkan. Karena semua fungsi di sini hanya bekerja pada string, versi Vulkan dikodekan oleh string yang dimulai dengan "vk_version_", diikuti oleh versi jurusan dan minor, dipisahkan oleh bagian bawah, seperti ini: "vk_version_1_0".
std::set<std::string> const & getDeviceExtensions(); Mendapatkan semua ekstensi perangkat yang ditentukan untuk platform saat ini. Perhatikan, bahwa tidak semuanya dapat didukung oleh perangkat yang sebenarnya.std::set<std::string> const & getInstanceExtensions(); Mendapat semua ekstensi instance yang ditentukan untuk platform saat ini. Perhatikan, bahwa tidak semuanya dapat didukung oleh contoh yang sebenarnya.std::map<std::string, std::string> const & getDeprecatedExtensions(); Mendapat peta semua ekstensi yang sudah usang ke ekstensi atau versi vulkan yang seharusnya menggantikan fungsi itu.std::map<std::string, std::vector<std::vector<std::string>>> const & getExtensionDepends( std::string const & extension ); Beberapa ekstensi tergantung pada ekstensi lain. Ketergantungan itu mungkin berbeda untuk versi Vulkan yang berbeda, dan mungkin ada berbagai set ketergantungan untuk versi Vulkan yang sama. Fungsi ini mendapatkan vektor vektor ekstensi per versi vulkan yang ekstensi yang diberikan tergantung.std::pair<bool, std::vector<std::vector<std::string>> const &> getExtensionDepends( std::string const & version, std::string const & extension ); Anggota first dari std::pair yang dikembalikan adalah benar, jika ekstensi yang diberikan ditentukan untuk versi Vulkan yang diberikan, jika tidak false . Anggota second dari std::pair yang dikembalikan adalah vektor vektor ekstensi, mendaftarkan set ekstensi terpisah yang ekstensi yang diberikan tergantung pada versi Vulkan yang diberikan.std::map<std::string, std::string> const & getObsoletedExtensions(); Mendapat peta dari semua ekstensi yang sudah usang ke ekstensi atau versi vulkan yang telah usang ekstensi itu.std::map<std::string, std::string> const & getPromotedExtensions(); Mendapat peta semua ekstensi yang dipromosikan ke ekstensi lain atau ke versi vulkan untuk perpanjangan versi Vulkan.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionDeprecatedBy( std::string const & extension ); Mendapatkan Ekstensi atau Versi Vulkan Ekstensi yang diberikan sudah usang.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionObsoletedBy( std::string const & extension ); Mendapatkan Ekstensi atau Versi Vulkan Ekstensi yang diberikan sudah usang.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionPromotedTo( std::string const & extension ); Mendapatkan Ekstensi atau Versi Vulkan Ekstensi yang diberikan dipromosikan.VULKAN_HPP_CONSTEXPR_20 bool isDeprecatedExtension( std::string const & extension ); Mengembalikan true jika ekstensi yang diberikan sudah usang oleh beberapa ekstensi lain atau versi vulkan.VULKAN_HPP_CONSTEXPR_20 bool isDeviceExtension( std::string const & extension ); Mengembalikan true jika ekstensi yang diberikan adalah ekstensi perangkat.VULKAN_HPP_CONSTEXPR_20 bool isInstanceExtension( std::string const & extension ); Mengembalikan true jika ekstensi yang diberikan adalah ekstensi instan.VULKAN_HPP_CONSTEXPR_20 bool isObsoletedExtension( std::string const & extension ); Mengembalikan true jika ekstensi yang diberikan sudah usang oleh beberapa ekstensi lain atau versi vulkan.VULKAN_HPP_CONSTEXPR_20 bool isPromotedExtension( std::string const & extension ); Mengembalikan true jika ekstensi yang diberikan dipromosikan ke beberapa ekstensi lain atau versi vulkan.Peringatan
Versi saat ini dari Microsoft Visual Studio 2022 tidak dapat menangani modul vulkan.cppm. Bug diajukan (https://developcercommunity.visualstudio.com/t/on-building-a-c20-hodule:-fatal-error/10469799#t-nd10485943). Anda setidaknya dapat menggunakan fitur ini jika Anda tidak membutuhkan atau ingin menggunakan vk::UniqueHandle atau vk::SharedHandle dengan mendefinisikan VULKAN_HPP_NO_SMART_HANDLE .
Vulkan-HPP menyediakan modul C ++ bernama, vulkan_hpp di vulkan.cppm . Modul C ++ dimaksudkan untuk menggantikan file header. Modul memiliki potensi untuk secara drastis meningkatkan waktu kompilasi untuk proyek -proyek besar, karena deklarasi dan definisi dapat dengan mudah dibagikan di seluruh unit terjemahan tanpa header penguraian berulang kali. Vulkan-HPP memiliki beberapa header yang sangat panjang (misalnya vulkan_structs.hpp ), dan modul C ++ cenderung mempersingkat waktu kompilasi untuk proyek yang saat ini menggunakannya.
Fitur ini membutuhkan kompiler terbaru dengan dukungan C ++ 20 lengkap:
cl.exe 19.28 atau lebih baru)Jika Anda bermaksud menggunakan dukungan modul C ++ CMake (dan mungkin ninja), maka alat yang lebih baru diperlukan:
cl.exe 19.34 atau lebih baru)Peringatan
Modul bernama Vulkan-HPP C ++ masih eksperimental. 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 . Misalnya:
# 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.
Berlisensi di bawah lisensi Apache, versi 2.0 ("lisensi"); Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. Anda dapat memperoleh salinan lisensi di
http://www.apache.org/licenses/LICENSE-2.0
Kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak yang didistribusikan di bawah lisensi didistribusikan berdasarkan "sebagaimana adanya", tanpa jaminan atau ketentuan dalam bentuk apa pun, baik tersurat maupun tersirat. Lihat lisensi untuk bahasa spesifik yang mengatur izin dan batasan di bawah lisensi.