الهدف من Vulkan-HPP هو توفير روابط C ++ فقط لـ Vulkan C API لتحسين تجربة Vulkan للمطورين دون تقديم تكلفة وقت تشغيل وحدة المعالجة المركزية. ويضيف ميزات مثل Type Safety for Enums و Bitfields ، ودعم حاوية STL ، والاستثناءات والتعدادات البسيطة.
| منصة | بناء الحالة |
|---|---|
| Linux |
Vulkan-HPP هو جزء من Lunarg Vulkan SDK منذ الإصدار 1.0.24. Just #include <vulkan/vulkan.hpp> وأنت مستعد لاستخدام روابط C ++. إذا كنت تستخدم إصدار Vulkan لم يدعمه بعد SDK Vulkan ، فيمكنك العثور على أحدث إصدار من الرؤوس هنا.
يتطلب Vulkan-HPP مترجم C ++ 11 قادرًا على التجميع. من المعروف أن المجمعين التاليين يعملون:
لإنشاء العينات والاختبارات المحلية ، سيتعين عليك استنساخ هذا المستودع وتشغيل cmake لإنشاء ملفات الإنشاء المطلوبة
تثبيت التبعيات.
افتح قذيفة توفر git واستنساخ المستودع مع:
git clone --recurse-submodules https://github.com/KhronosGroup/Vulkan-Hpp.git
قم بتغيير الدليل الحالي إلى دليل Vulkan-HPP الذي تم إنشاؤه حديثًا.
إنشاء بيئة بناء مع 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
قد تحتاج إلى تحديد مولد عبر -G ، للحصول على قائمة كاملة من المولدات تنفذ cmake -G .
vulkan.hpp من ملف تسجيل vk.xml XML ، أضف -DVULKAN_HPP_RUN_GENERATOR=ON Option to the Cmake Order. إما أن تفتح المشروع الذي تم إنشاؤه باستخدام IDE ، مثل Visual Studio أو تشغيل عملية الإنشاء باستخدام cmake --build build --parallel .
اختياري: لتحديث Vulkan-HPP وفيروساتها الفرعية تنفذ git pull --recurse-submodules .
يمكنك تنزيل وتثبيت Vulkan-HPP باستخدام مدير التبعية VCPKG:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install vulkan-headersيتم الاحتفاظ بمنفذ Vulkan-HPP في VCPKG من قبل أعضاء فريق Microsoft والمساهمين في المجتمع. إذا كان الإصدار قديمًا ، فيرجى إنشاء مشكلة أو سحب طلب على مستودع VCPKG.
إذا تم العثور على برنامج clang-format بواسطة CMake ، يتم تعيين Define CLANG_FORMAT_EXECUTABLE وفقًا لذلك. في هذه الحالة ، يتم تنسيق vulkan.hpp الذي تم إنشاؤه باستخدام ملف .clang-format الموجود في الدليل الجذر لهذا المشروع. خلاف ذلك ، يتم تنسيقها على أنها مشفرة في المولد.
يوفر File VulkanHpp.natvis طريقة عرض مخصصة على vk::Flags for Visual Studio. إذا قمت بإضافة هذا الملف إلى دليل NATVIS الخاص بالمستخدم لتثبيت Visual Studio الخاص بك (٪ userprofile ٪ Documents Visual Studio 2022 Visualizers) ، فستحصل على vk::Flags بشكل جيد في مصحح الأخطاء الخاصة بك مع جميع مشاريع الاستوديو المرئية الخاصة بك.
لتجنب تصادمات الاسم مع Vulkan C API ، توجد روابط C ++ في مساحة اسم vk . تنطبق القواعد التالية على التسمية الجديدة:
Vk . بالإضافة إلى هذا الحرف الأول من الوظائف هو الحالة الأدنى.vkCreateInstance كـ vk::createInstance .VkImageTiling كـ vk::ImageTiling .VkImageCreateInfo كـ vk::ImageCreateInfo .VK_ وإزالة النوع infix. إذا كان نوع التعداد امتدادًا ، فقد تمت إزالة لاحقة التمديد من قيم التعداد.في جميع الحالات الأخرى ، لم تتم إزالة لاحقة التمديد.
VK_IMAGETYPE_2D الآن vk::ImageType::e2D .VK_COLOR_SPACE_SRGB_NONLINEAR_KHR هو الآن vk::ColorSpaceKHR::eSrgbNonlinear .VK_STRUCTURE_TYPE_PRESENT_INFO_KHR الآن vk::StructureType::ePresentInfoKHR ._BIT تمت إزالةها أيضًا. في بعض الحالات ، قد يكون من الضروري نقل Vulkan-HPP إلى مساحة اسم مخصصة. يمكن تحقيق ذلك من خلال تحديد VULKAN_HPP_NAMESPACE قبل تضمين Vulkan-HPP.
تعلن Vulkan-HPP فئة لجميع المقابض لضمان سلامة النوع الكامل وإضافة دعم لوظائف الأعضاء على المقابض. تمت إضافة وظيفة عضو إلى فئة مقبض لكل وظيفة تقبل المقبض المقابل كمعلمة أولى. بدلاً من vkBindBufferMemory(device, ...) يمكن للمرء أن يكتب device.bindBufferMemory(...) أو vk::bindBufferMemory(device, ...) .
هناك رأس إضافي يدعى vulkan_raii.hpp تم إنشاؤه. يحمل هذا الرأس فئات غلاف متوافقة مع RAII لأنواع المقبض. وهذا هو ، على سبيل المثال نوع المقبض VkInstance ، هناك غلاف متوافق مع RAII vk::raii::Instance . يرجى إلقاء نظرة على العينات باستخدام تلك الفصول في دليل RAII_Samples.
على منصات 64 بت Vulkan-HPP تدعم التحويلات الضمنية بين مقابض C ++ Vulkan ومقابض C Vulkan. على المنصات 32 بت ، يتم تعريف جميع المقابض غير القابلة للانتشار على أنها uint64_t ، وبالتالي منع اختبارات التحول في النوع في وقت الترجمة والتي من شأنها أن تحضر الواجبات بين أنواع المقبض غير المتوافقة. نظرًا لذلك ، لا يمكّن Vulkan-HPP التحويل الضمني للمنصات 32 بت افتراضيًا ويوصى باستخدام static_cast لتحويل مثل هذا: VkImage = static_cast<VkImage>(cppImage) لمنع تحويل بعض التعريفي إلى مقبض أو عكس بالعكس. إذا كنت تقوم بتطوير الكود الخاص بك على منصة 64 بت ، ولكنك ترغب في تجميع الكود الخاص بك لمنصة 32 بت دون إضافة قوالب صريحة ، يمكنك تحديد VULKAN_HPP_TYPESAFE_CONVERSION إلى 1 في نظام البناء الخاص بك أو قبل تضمين vulkan.hpp . على منصات 64 بت تم تعيين هذا تعريف هذا على 1 بشكل افتراضي ويمكن ضبطه على 0 لتعطيل التحويلات الضمنية.
تضيف ميزة التعداد الناطقة سلامة النوع إلى الأعلام ، ولكنها تمنع أيضًا استخدام أجزاء العلم كمدخلات لعمليات bitwise مثل & | .
كما الحلول Vulkan-HPP يوفر قالب الفصل vk::Flags الذي يجلب العمليات القياسية مثل &= ، |= ، & ، و | إلى التعدادات لدينا. باستثناء التهيئة مع 0 يتصرف هذه الفئة تمامًا مثل خلفية bitmass العادية مع التحسن الذي من المستحيل تعيين أجزاء غير محددة بواسطة التعداد المقابل عن طريق الحادث. فيما يلي بعض الأمثلة لمعالجة 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 */ , ...); عند إنشاء مقبض في Vulkan ، عادة ما يتعين على المرء إنشاء بنية CreateInfo التي تصف المقبض الجديد. يمكن أن يؤدي ذلك إلى رمز طويل جدًا كما يتضح في مثال 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); هناك مشكلتان نموذجيتان يواجههما مطورو فولكان عند ملء حقل بنية CreateInfo حسب الحقل:
sType غير صحيح.خاصة الأولى يصعب اكتشافها.
يوفر Vulkan-HPP منشئات لجميع كائنات CreateInfo التي تقبل معلمة واحدة لكل متغير عضو. وبهذه الطريقة ، يلقي برنامج التحويل البرمجي خطأ المترجم إذا تم نسيان قيمة. بالإضافة إلى هذا sType يتم ملء تلقائيا بالقيمة الصحيحة وتعيين pNext على nullptr افتراضيا. إليك كيف يبدو نفس الرمز مع مُنشئ:
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); مع منشئات CreateInfo ، يمكن للمرء أيضًا تمرير المنشأ إلى وظائف Vulkan مثل هذه:
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}); بدءًا من C ++ 20 ، يدعم C ++ التهيئة المحددة. نظرًا لأن هذه الميزة تتطلب عدم وجود أي منشئات معلنة أو موروثة من المستخدمين ، يجب عليك #define VULKAN_HPP_NO_CONSTRUCTORS ، التي تزيل جميع بنية البنود والاتحاد من vulkan.hpp . بدلاً من ذلك ، يمكنك بعد ذلك استخدام التهيئة الكلية. قد تبدو خطوط VK القليلة الأولى في مصدرك مثل:
// 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 };بدلاً من
// 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); لاحظ أن أمر المصمم يحتاج إلى مطابقة أمر الإعلان. لاحظ أيضًا ، يمكنك الآن تعيين عضو sType في هيكل VK. هذا ليس ضروريًا (حيث يتم تهيئته بشكل صحيح افتراضيًا) ولا يوصى به.
يحتوي API Vulkan API على عدة أماكن تتطلب (العد ، المؤشر) كسياستين للوظيفة ، ويحتوي C ++ على عدد قليل من الحاويات التي تعرض لهذا الزوج تمامًا. لتبسيط التطوير ، حلت روابط Vulkan-HPP محل أزواج الوسيطة هذه مع قالب فئة vk::ArrayProxy الذي يقبل المصفوفات الفارغة وقيمة واحدة وكذلك حاويات STL std::initializer_list ، std::array و std::vector ascition للبناء. وبهذه الطريقة ، يمكن أن تقبل إصدار Vulkan الذي تم إنشاؤه منفردًا مجموعة متنوعة من المدخلات دون وجود انفجار combinatoric الذي قد يحدث عند إنشاء وظيفة لكل نوع حاوية.
فيما يلي بعض عينات التعليمات البرمجية حول كيفية استخدام 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 يولد مراجع للمؤشرات للهياكل. يسمح هذا التحويل بتمرير الهياكل المؤقتة إلى الوظائف التي يمكن أن تؤدي إلى رمز أقصر. في حالة اختياري الإدخال ، وبالتالي قبول مؤشر فارغ ، سيكون نوع المعلمة هو vk::Optional<T> const& . يقبل هذا النوع إما إشارة إلى T أو nullptr كمدخل ، وبالتالي يسمح بهياكل مؤقتة اختيارية.
// 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 بسلاسل الهياكل من خلال مؤشر pNext . لدى Vulkan-HPP قالب فئة متغير يسمح ببناء سلاسل الهيكل هذه بأقل الجهود. بالإضافة إلى ذلك ، يتحقق في وقت الترجمة إذا سمحت المواصفات ببناء سلسلة 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 مُنشئًا لهذه السلاسل المشابهة لكائنات CreateInfo التي تقبل قائمة جميع الهياكل من السلسلة. يتم ضبط حقل pNext تلقائيًا على القيمة الصحيحة:
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> c = {
vk::MemoryAllocateInfo (size, type),
vk::MemoryDedicatedAllocateInfo (image)
}; إذا كان من المقرر إزالة أحد هياكل الهيكل المباني ، وربما بسبب بعض الإعدادات الاختيارية ، يمكنك استخدام الدالة vk::StructureChain::unlink<ClassType>() . إنه يعدل الهيكل الهيكلي بحيث لم يعد الهيكل المحدد جزءًا من سلسلة pnext بعد الآن. لاحظ أن تخطيط الذاكرة الفعلي للبنية الهيكلية لا يتم تعديله بواسطة هذه الوظيفة. في حال كان يجب إعادة إضافة الهيكل نفسه إلى The structurechain مرة أخرى ، استخدم vk::StructureChain::relink<ClassType>() .
في بعض الأحيان ، يتعين على المستخدم تمرير سلسلة هيكل مسببة للاستعلام عن المعلومات. بالنسبة لتلك الحالات ، هناك وظيفتان getter المقابلان. واحد مع قالب متغير يولد سلسلة بنية من عنصرين على الأقل لبناء قيمة الإرجاع:
// 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>();للحصول على الهيكل الأساسي فقط ، دون التسلسل ، فإن وظيفة getter الأخرى المقدمة لا تحتاج إلى وسيطة قالب للهيكل للحصول على:
// Query just vk::MemoryRequirements2KHR
vk::MemoryRequirements2KHR memoryRequirements = device.getBufferMemoryRequirements2KHR({}); بشكل افتراضي ، يتم تمكين Vulkan-HPP. هذا يعني أن Vulkan-HPP يتحقق من رمز الإرجاع لكل استدعاء دالة والذي يعيد vk::Result . إذا كان vk::Result هو فشل ، فسيتم طرح std::runtime_error . نظرًا لعدم حاجة إلى إرجاع رمز الخطأ بعد الآن ، يمكن الآن لإرجاع روابط C ++ إلى إرجاع قيمة الإرجاع الفعلية المطلوبة ، أي مقبض Vulkan. في هذه الحالات ، يتم تعريف vk::ResultValue<SomeType>::type على أنه النوع الذي تم إرجاعه.
لإنشاء جهاز يمكنك الآن الكتابة:
vk::Device device = physicalDevice.createDevice(createInfo); تسمح بعض الوظائف بأكثر من مجرد vk::Result::eSuccess تعتبر رمز نجاح. لهذه الوظائف ، نعيد دائمًا vk::ResultValue<SomeType> . مثال على ذلك هو acquireNextImage2KHR ، يمكن استخدامه مثل هذا:
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 ;
} مع مرور الوقت ، قد تتغير بعض وظائف Vulkan ، بحيث تبدأ في دعم المزيد من رموز النتائج من vk::Result::eSuccess كرمز النجاح. لن يكون هذا التغيير المنطقي مرئيًا في C API ، ولكن في API C ++ ، لأن هذه الوظيفة ستعيد الآن vk::ResultValue<SomeType> بدلاً من مجرد SomeType . في مثل هذه الحالات (النادرة) ، سيتعين عليك ضبط مقاطع CPP لتعكس تغيير واجهة برمجة التطبيقات.
إذا تم تعطيل معالجة الاستثناءات عن طريق تحديد VULKAN_HPP_NO_EXCEPTIONS ، فإن نوع vk::ResultValue<SomeType>::type هو هيكل يحمل vk::Result SomeType . يدعم هذا الهيكل تفريغ قيم الإرجاع باستخدام std::tie .
في حال كنت لا ترغب في استخدام vk::ArrayProxy وتحويل قيمة الإرجاع ، لا يزال بإمكانك استدعاء وظيفة على غرار C العادي. فيما يلي ثلاثة أمثلة توضح 3 طرق لاستخدام واجهة برمجة التطبيقات:
يوضح المقتطف الأول كيفية استخدام واجهة برمجة التطبيقات دون استثناءات وتحول قيمة الإرجاع:
// 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 ?
}يوضح المقتطف الثاني كيفية استخدام واجهة برمجة التطبيقات باستخدام تحويل قيمة الإرجاع ، ولكن بدون استثناءات. إنه بالفعل أقصر قليلاً من الكود الأصلي:
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 ?
}طريقة أجمل لتفريغ النتيجة هي استخدام الارتباطات المنظمة في C ++ 17. سوف يسمحون لنا بالحصول على النتيجة مع سطر واحد من التعليمات البرمجية:
auto [result, shaderModule2] = device.createShaderModule({...} /* createInfo temporary */ );أخيرًا ، فإن مثال الرمز الأخير هو استخدام الاستثناءات وتحويل قيمة الإرجاع. هذا هو الوضع الافتراضي لواجهة برمجة التطبيقات.
vk::ShaderModule shader1;
vk::ShaderModule shader2;
try
{
shader1 = device. createShaderModule ({...});
shader2 = device. createShaderModule ({...});
}
catch (std:: exception const &e)
{
// handle error and free resources
}مهم
لا تدعم Vulkan مع Fulkan في vk -Namespace RAII ، وبالتالي تحتاج إلى تنظيف مواردك في معالج الأخطاء! بدلاً من ذلك ، يمكنك استخدام فئات غلاف المقبض في vk::raii -NamesPace.
مع C ++ 17 وما فوق ، تُعزى بعض الوظائف مع [[nodiscard]] ، مما يؤدي إلى تحذير إذا لم تستخدم قيمة الإرجاع بأي شكل من الأشكال. يمكنك إيقاف تشغيل هذه التحذيرات عن طريق تحديد VULKAN_HPP_NO_NODISCARD_WARNINGS .
لتحويل قيمة الإرجاع ، هناك فئة واحدة خاصة من قيم الإرجاع التي تتطلب معالجة خاصة: التعدادات. للتعدادات ، عادة ما يتعين عليك كتابة رمز مثل هذا:
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);نظرًا لأن كتابة هذه الحلقة مرارًا وتكرارًا تكون شاقة وخيطًا عرضة لربط C ++ يعتني بالتعداد حتى تتمكن من الكتابة فقط:
std::vector<LayerProperties> properties = physicalDevice.enumerateDeviceLayerProperties(); يوفر Vulkan-HPP واجهة vk::UniqueHandle<Type, Deleter> . لكل نوع مقبض Vulkan vk::Type يوجد مقبض فريد من نوعه vk::UniqueType والذي سيحذف مورد Vulkan الأساسي عند التدمير ، على سبيل المثال vk::UniqueBuffer هو المقبض الفريد لـ vk::Buffer .
لكل وظيفة تقوم ببناء مقبض Vulkan من النوع vk::Type Vulkan-HPP يوفر إصدارًا ثانيًا يعيد vk::UniqueType . على سبيل المثال لـ vk::Device::createBuffer هناك vk::Device::createBufferUnique و vk::allocateCommandBuffers هناك vk::allocateCommandBuffersUnique .
لاحظ أن استخدام vk::UniqueHandle يأتي بتكلفة حيث يتعين على معظم الحذف تخزين vk::AllocationCallbacks ومقبض الأصل المستخدمة للبناء لأنها مطلوبة للتدمير التلقائي.
يوفر Vulkan-HPP واجهة vk::SharedHandle<Type> . لكل نوع مقبض Vulkan vk::Type يوجد مقبض مشترك vk::SharedType والذي سيحذف مورد Vulkan الأساسي عند التدمير ، على سبيل المثال vk::SharedBuffer هو المقبض المشترك لـ vk::Buffer .
على عكس vk::UniqueHandle ، يأخذ vk::SharedHandle ملكية مشتركة للمورد وكذلك والديه. هذا يعني أنه لن يتم تدمير مقبض الوالدين حتى يتم حذف جميع موارد الأطفال. هذا مفيد للموارد التي يتم مشاركتها بين مؤشرات الترابط أو الكائنات المتعددة.
تضمن هذه الآلية أمر التدمير الصحيح حتى لو تم تدمير الوالد vk::SharedHandle قبل مقبض طفلها. خلاف ذلك ، يتصرف المقبض مثل std::shared_ptr . يجب أن يكون vk::SharedInstance أو أي من الأشياء الفرعية في حذفه (تم إنشاؤه أولاً ، أولاً في إعلان الفصل).
لا توجد وظائف تُرجع vk::SharedHandle مباشرة. بدلاً من ذلك ، يمكنك إنشاء vk::SharedHandle من vk::Handle :
vk::Buffer buffer = device.createBuffer(...);
vk::SharedBuffer sharedBuffer (buffer, device); // sharedBuffer now owns the buffer هناك العديد من التخصصات لـ vk::SharedHandle لأنواع المقبض المختلفة. على سبيل المثال ، قد يأخذ vk::SharedImage وسيطة إضافية لتحديد ما إذا كانت الصورة مملوكة لـ 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 هناك أيضًا تخصص لـ vk::SwapchainKHR الذي يأخذ وسيطة إضافية لتحديد السطح:
vk::SwapchainKHR swapchain = device.createSwapchainKHR(...);
vk::SharedSwapchainKHR sharedSwapchain (swapchain, device, surface); // sharedSwapchain now owns the swapchain and surface يمكنك إنشاء تحميل زائد vk::SharedHandle لنوع المقبض الخاص بك أو مقابض مشتركة خاصة من خلال توفير العديد من وسيطات القالب إلى SharedHandleBase :
مع هذا ، قدم وظيفة تدمير ثابتة مخصصة internalDestroy ، والتي تأخذ في مقبض أولياء الأمور ومقبض لتدمير. لا تنس إضافة إعلان صديق للصف الأساسي.
// 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 لتوفير وظائف الخلق في المستقبل.
بالإضافة إلى vk::UniqueHandles و vk::SharedHandles ، هناك مجموعة من فئات التفاف لجميع أنواع المقابض التي تتبع RAII-Paradigm (اكتساب الموارد هي التهيئة) ، المقدمة في مساحة اسم vk::raii .
على الرغم من أن vk::UniqueHandle يحاكي مقبض ملفوف بواسطة std::unique_ptr ، و vk::SharedHandle محاكاة مقبض ملفوف بواسطة std::shared_ptr ، بما في ذلك معلومات الوالدين ، vk::raii::Handle هو مجرد فئة تكتسب vk intructor في حدوثها وتستوعبها في تدميرها. وبالتالي ، فأنت حر في استخدامها كقيم أو لفها مع بعض المؤشر الذكي.
بخلاف vk::Handles ، تحتاج جميع فئات غلاف المقبض هذه إلى الاحتفاظ ببيانات إضافية ، وبالتالي ليست متطابقة ثنائية مع المقابض C Vulkan.
نظرًا لأن vk::UniqueHandles و vk::SharedHandles يستخدمون نفس المرسل مثل vk::Handles ، يمكن خلطها بسهولة ومطابقة. تستخدم vk::raii::Handles بعض المرسلين المختلفين قليلاً وبالتالي لا تتوافق مع الموجهات الأخرى! هذا هو ، بالنسبة vk-Handles ، و vk::UniqueHandles ، و vk::SharedHandles ، تحتاج إلى إنشاء إنشاء مرسل عالمي كما هو موضح في https://github.com/khronosgroup/vulkan-hpp#extensions-per-device-functions. بالنسبة vk::raii-Handles ، فإن هذا ليس هناك حاجة ، لأنهم يحافظون على مرسليهم. الميزة الكبيرة هنا هي عندما يكون لديك أجهزة متعددة: الوظائف التي تسمى عبر vk::raii-Handles تتصل دائمًا بالوظائف الخاصة بالجهاز.
في بعض الأحيان يكون من الضروري استخدام std::vector مع مخصصات مخصصة. يدعم Vulkan-HPP المتجهات ذات المخصصات المخصصة كمدخلات لـ vk::ArrayProxy وللوتمات التي تعيد المتجه. للحالة الأخيرة ، أضف مخصصك المفضل المخصص كوسيطة قالب إلى استدعاء الوظيفة مثل هذا:
std::vector<LayerProperties, MyCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties<MyCustomAllocator>(); يمكنك أيضًا استخدام مخصص مخصص مخصص من خلال توفيره كوسيطة لتلك الوظائف. لسوء الحظ ، لجعل المترجمين سعداء ، تحتاج أيضًا إلى تعيين وسيطة الإرسال صراحة. للحصول على الافتراضي هناك ، يكفي {} :
MyStatefulCustomAllocator allocator;
std::vector<LayerProperties, MyStatefulCustomAllocator> properties = physicalDevice.enumerateDeviceLayerProperties(allocator, {}); في جميع أنحاء vulkan.hpp ، هناك بضع مكالمات إلى وظيفة التأكيد. من خلال تحديد VULKAN_HPP_ASSERT ، يمكنك تحديد وظيفة تأكيدك المخصصة التي سيتم استدعاؤها بدلاً من ذلك.
بشكل افتراضي ، سيتم استخدام VULKAN_HPP_ASSERT_ON_RESULT للتحقق من النتائج عند تحديد VULKAN_HPP_NO_EXCEPTIONS . إذا كنت ترغب في التعامل مع الأخطاء بنفسك ، فيمكنك تعطيل/تخصيصها تمامًا مثل VULKAN_HPP_ASSERT .
هناك بضعة تأكيدات ثابتة لكل فئة مقبض وكل بنية في الملف vulkan_static_assertions.hpp . يمكنك تضمين هذا الملف في واحد على الأقل من ملفاتك المصدر. من خلال تحديد VULKAN_HPP_STATIC_ASSERT ، يمكنك تحديد التأكيد الثابت المخصص الخاص بك لاستخدامه في هذه الحالات. وهذا هو ، من خلال تعريفه ليكون NOP ، يمكنك تقليل وقت التجميع الخاص بك قليلا.
يوضح اللودر Vulkan فقط وظائف Vulkan الأساسية وعدد محدود من الامتدادات. لاستخدام Vulkan-HPP مع امتدادات ، يجب أن يكون لديك إما مكتبة توفر شبعات لجميع وظائف Vulkan المستخدمة أو لإخبار Vulkan-HPP بإرسال هذه الوظائف المؤشرات. يوفر Vulkan-HPP آلية إرسال لكل وظيفة من خلال قبول فئة الإرسال كمعلمة الأخيرة في كل استدعاء دالة. يجب أن توفر فئة الإرسال نوعًا قابل للاتصال لكل وظيفة Vulkan المستخدمة. يوفر Vulkan-HPP تطبيقًا واحدًا ، DispatchLoaderDynamic ، والذي يجلب جميع مؤشرات الوظائف المعروفة للمكتبة.
// 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); لاستخدام vk::DispatchLoaderDynamic كمرسل افتراضي (يعني: لا تحتاج إلى إضافته بشكل صريح إلى كل مكالمة دالة) ، فأنت بحاجة إلى #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 ، والحصول على macro VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE بشكل دقيق. ثم يمكنك استخدامه بواسطة Macro VULKAN_HPP_DEFAULT_DISPATCHER ، كما هو موضح في قصاصات الكود أدناه. إنشاء vk::DispatchLoaderDynamic بالكامل مميز ، هو عملية من خطوتين إلى ثلاث خطوات ، حيث لديك ثلاثة خيارات للخطوة الأولى:
vk::DynamicLoader : VULKAN_HPP_DEFAULT_DISPATCHER.init();getProcAddress (قارن مع vk::DynamicLoader في vulkan.hpp ): YourDynamicLoader ydl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(ydl);ملحوظة
تحتاج إلى الحفاظ على كائن المحمل الديناميكي هذا على قيد الحياة حتى بعد آخر مكالمة إلى وظيفة Vulkan في البرنامج. على سبيل المثال من خلال جعله ثابتًا ، أو تخزينه على مستوى العالم.
PFN_vkGetInstanceProcAddr : PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = your_own_function_pointer_getter();
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);vk::Instance للحصول على جميع مؤشرات الوظائف الأخرى: vk::Instance instance = vk::createInstance({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);vk::Device للحصول على مؤشرات وظائف خاصة بالجهاز std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
assert (!physicalDevices.empty());
vk::Device device = physicalDevices[ 0 ].createDevice({}, nullptr );
VULKAN_HPP_DEFAULT_DISPATCHER.init(device);بعد الخطوة الثانية أعلاه ، يكون المرسل يعمل بكامل طاقته. يمكن أن تؤدي إضافة الخطوة الثالثة إلى برمز أكثر كفاءة. ولكن إذا كنت تنوي استخدام أجهزة متعددة ، فيمكنك فقط حذف هذه الخطوة الثالثة والسماح للسائق بإلغاء توسيع الجهاز.
في بعض الحالات ، يجب تضمين التخزين لـ DispatchLoaderDynamic في DLL. بالنسبة لتلك الحالات ، تحتاج إلى تحديد VULKAN_HPP_STORAGE_SHARED لإخبار Vulkan-HPP بأن التخزين موجود في DLL. عند تجميع DLL مع التخزين ، يلزم أيضًا تحديد VULKAN_HPP_STORAGE_SHARED_EXPORT لتصدير الرموز المطلوبة.
بالنسبة لجميع الوظائف ، فإن VULKAN_HPP_DEFAULT_DISPATCHER هو الافتراضي للوسيطة الأخيرة لتلك الوظيفة. إذا كنت ترغب في توفير المرسل بشكل صريح لكل استدعاء دالة (عندما يكون لديك عدة مرسلين لأجهزة مختلفة ، على سبيل المثال) وتريد التأكد من أنك لا تفوت أي مكالمة وظيفية ، يمكنك تحديد VULKAN_HPP_NO_DEFAULT_DISPATCHER قبل أن تضم vulkan.hpp لإزالة هذه الجدال.
يوفر vulkan.hpp بعض السمات من النوع ، وتخفيف النماذج metaprogramming:
template <typename EnumType, EnumType value> struct CppType maps IndexType قيم ( IndexType::eUint16 ، IndexType::eUint32 ، ...) إلى النوع المقابل ( uint16_t ، uint32_t ، ...) حسب Type العضو ؛ Maps ObjectType قيم ( ObjectType::eInstance ، ObjectType::eDevice ، ...) إلى النوع المقابل ( vk::Instance ، vk::Device ، ...) حسب Type العضو ؛ خرائط DebugReportObjectType قيم ( DebugReportObjectTypeEXT::eInstance ، DebugReportObjectTypeEXT::eDevice ، ...) إلى النوع المقابل ( vk::Instance ، vk::Device ، ...) من نوع العضو Type ؛template <typename T> struct IndexTypeValue أنواع العددية ( uint16_t ، uint32_t ، ...) إلى قيمة IndexType المقابلة ( IndexType::eUint16 ، IndexType::eUint32 ، ...).template <typename T> struct isVulkanHandleType نوعًا ما إلى true إذا وفقط إذا كان فئة مقبض ( vk::Instance ، vk::Device ...) بواسطة value العضو الثابت.HandleClass::CType Mappe فئة مقبض ( vk::Instance ، vk::Device ...) إلى نوع C المقابل ( VkInstance ، VkDevice ، ...) بواسطة نوع العضو CType .HandleClass::objectType يقوم بتعيين فئة مقبض ( vk::Instance ، vk::Device ...) إلى قيمة ObjectType المقابل ( ObjectType::eInstance ، ObjectType::eDevice ، ...) بواسطة abome objectType static.HandleClass::debugReportObjectType Mappe فئة مقبض ( vk::Instance ، vk::Device :: ...) إلى قيمة DebugReportObjectTypeEXT المقابلة ( DebugReportObjectTypeEXT::eInstance ، DebugReportObjectTypeEXT::eDevice ، ...) بواسطة debugReportObjectType . مع الرأس الإضافي vulkan_format_traits.hpp ، يتم توفير بضع وظائف للسمات على vk::Format . مع C ++ 14 وما فوق ، يتم تمييز كل هذه الوظائف على أنها constexpr ، وهذا هو مع الحجج المناسبة ، يتم حلها في وقت الترجمة.
uin8_t blockSize( vk::Format format ); يحصل على حجم كتلة Texel لهذا التنسيق في البايتات.uint8_t texelsPerBlock( vk::Format format ); يحصل على عدد Texels في كتلة Texel.std::array<uint8_t, 3> blockExtent( vk::Format format ); يحصل على المدى ثلاثي الأبعاد للكتل Texel.char const * compressionScheme( vk::Format format ); يحصل على وصف نصي لنظام الضغط لهذا التنسيق ، أو نص فارغ إذا لم يتم ضغطه.bool isCompressed( vk::Format format ); صحيح ، إذا كان التنسيق عبارة عن تنسيق مضغوط ، وإلا خطأ.uint8_t packed( vk::Format format ); يحصل على عدد البتات التي يعبئها التنسيق. يمكن تخزين عنصر صورة واحد في هذا التنسيق في نفس المساحة مثل نوع العدد من هذا العرض.uint8_t componentCount( vk::Format format ); يحصل على عدد مكونات هذا التنسيق.bool componentsAreCompressed( vk::Format format ); صحيح ، إذا كانت مكونات هذا التنسيق مضغوطًا ، بطريقة خاطئة.uint8_t componentBits( vk::Format format, uint8_t component ); يحصل على عدد البتات في هذا المكون ، إن لم يكن مضغوطًا ، وإلا 0.char const * componentName( vk::Format format, uint8_t component ); يحصل على اسم هذا المكون كسلسلة C.char const * componentNumericFormat( vk::Format format, uint8_t component ); يحصل على التنسيق الرقمي لهذا المكون كسلسلة C.uint8_t componentPlaneIndex( vk::Format format, uint8_t component ); يحصل على فهرس الطائرة ، هذا المكون يكمن.uint8_t planeCount( vk::Format format ); يحصل على عدد طائرات الصور لهذا التنسيق.vk::Format planeCompatibleFormat( vk::Format format, uint8_t plane ); يحصل على تنسيق من طائرة واحدة متوافق مع هذه الطائرة.uint8_t planeHeightDivisor( vk::Format format, uint8_t plane ); يحصل على الارتفاع النسبي لهذه الطائرة. قيمة k تعني أن هذه الطائرة هي 1/k ارتفاع التنسيق الكلي.uint8_t planeWidthDivisor( vk::Format format, uint8_t plane ); يحصل على العرض النسبي لهذه الطائرة. قيمة k تعني أن هذه الطائرة هي 1/k عرض التنسيق الكلي. مع رأس vulkan_hash.hpp إضافي ، يمكنك الحصول على تخصصات من std::hash لفئات غلاف المقبض ، ومع C ++ 14 ، لأغلفة الهيكل. مع VULKAN_HPP_HASH_COMBINE ، يمكنك تحديد تجمع التجزئة الخاصة بك بين خوارزمية لعناصر الهيكل.
مع الرأس الإضافي vulkan_extension_inspection.hpp ، يتم توفير بعض الوظائف لفحص الامتدادات. مع C ++ 20 وما فوق ، يتم تمييز بعض هذه الوظائف على أنها constexpr ، وهذا هو مع الحجج المناسبة ، يتم حلها في وقت الترجمة. يتم تحديد كل امتداد بواسطة سلسلة تحمل اسمها. لاحظ أنه يوجد تعريف مع هذا الاسم لكل امتداد. قد توفر بعض الوظائف معلومات تعتمد على إصدار Vulkan. نظرًا لأن جميع الوظائف هنا تعمل فقط على الأوتار ، يتم ترميز إصدارات Vulkan بسلسلة تبدأ بـ "VK_VERVER_" ، تليها الإصدار الرئيسي والبسيط ، مفصولة بواسطة undersore ، مثل هذا: "VK_VERVER_1_0".
std::set<std::string> const & getDeviceExtensions(); يحصل على جميع ملحقات الجهاز المحددة للمنصة الحالية. لاحظ أنه لا يمكن دعم جميعهم من قبل الأجهزة الفعلية.std::set<std::string> const & getInstanceExtensions(); يحصل على جميع ملحقات المثيل المحددة للنظام الأساسي الحالي. لاحظ أنه لا يمكن دعمهم جميعًا من الحالات الفعلية.std::map<std::string, std::string> const & getDeprecatedExtensions(); يحصل على خريطة لجميع الامتدادات التي تم إهمالها إلى النسخة التمديد أو Vulkan التي من المفترض أن تحل محل هذه الوظيفة.std::map<std::string, std::vector<std::vector<std::string>>> const & getExtensionDepends( std::string const & extension ); بعض الامتدادات تعتمد على امتدادات أخرى. قد تختلف التبعيات عن إصدارات Vulkan المختلفة ، وقد تكون هناك مجموعات مختلفة من التبعيات لنفس إصدار Vulkan. تحصل هذه الوظيفة على متجه من متجهات الامتدادات لكل إصدار من الفلكان يعتمد عليه الامتداد المحدد.std::pair<bool, std::vector<std::vector<std::string>> const &> getExtensionDepends( std::string const & version, std::string const & extension ); العضو first في std::pair تم إرجاعه صحيح ، إذا تم تحديد الامتداد المحدد لإصدار Vulkan المعطى ، وإلا false . العضو second من std::pair الذي تم إرجاعه هو متجه ناقلات الامتدادات ، حيث يسرد المجموعات المنفصلة من الامتدادات التي يعتمد عليها الامتداد المحدد على النسخة الموثوقة المحددة.std::map<std::string, std::string> const & getObsoletedExtensions(); يحصل على خريطة لجميع الامتدادات المفقودة إلى النسخة التمديد أو Vulkan التي أوقضت هذا الامتداد.std::map<std::string, std::string> const & getPromotedExtensions(); يحصل على خريطة لجميع الامتدادات التي تم ترقيتها إلى امتداد آخر أو إلى نسخة فولكان إلى هذا الامتداد من إصدار Vulkan.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionDeprecatedBy( std::string const & extension ); الحصول على ملحق أو إصدار Vulkan تم إهمال الامتداد المحدد بواسطة.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionObsoletedBy( std::string const & extension ); يحصل على تمديد أو نسخة Vulkan تم أوفق الامتداد المحدد بواسطة.VULKAN_HPP_CONSTEXPR_20 std::string getExtensionPromotedTo( std::string const & extension ); الحصول على ملحق أو نسخة Vulkan يتم الترويج للملحق المحدد إلى.VULKAN_HPP_CONSTEXPR_20 bool isDeprecatedExtension( std::string const & extension ); إرجاع true إذا تم إهمال الامتداد المحدد من خلال بعض النسخة الأخرى أو Vulkan.VULKAN_HPP_CONSTEXPR_20 bool isDeviceExtension( std::string const & extension ); إرجاع true إذا كان الامتداد المحدد هو امتداد للجهاز.VULKAN_HPP_CONSTEXPR_20 bool isInstanceExtension( std::string const & extension ); إرجاع true إذا كان الامتداد المحدد هو امتداد مثيل.VULKAN_HPP_CONSTEXPR_20 bool isObsoletedExtension( std::string const & extension ); إرجاع true إذا كان الامتداد المحدد قد أوفق من قبل بعض الامتداد أو نسخة Vulkan الأخرى.VULKAN_HPP_CONSTEXPR_20 bool isPromotedExtension( std::string const & extension ); إرجاع true إذا تم الترويج للتمديد المحدد إلى نسخة أخرى أو نسخة أخرى من الفلكان.تحذير
الإصدار الحالي من Microsoft Visual Studio 2022 غير قادر على التعامل مع وحدة Vulkan.cppm. يتم تقديم خطأ (https://developercommunity.visualstudio.com/t/on-building-a-c20-module:-fatal-error/10469799#t-nd10485943). يمكنك على الأقل استخدام هذه الميزة إذا لم تكن بحاجة أو ترغب في استخدام vk::UniqueHandle أو vk::SharedHandle من خلال تحديد VULKAN_HPP_NO_SMART_HANDLE .
يوفر Vulkan-HPP وحدة C ++ المسماة ، vulkan_hpp في vulkan.cppm . تهدف وحدات C ++ إلى محل ملفات الرأس. الوحدات النمطية لديها القدرة على تحسين أوقات التجميع بشكل كبير للمشاريع الكبيرة ، حيث يمكن مشاركة الإعلانات والتعاريف بسهولة عبر وحدات الترجمة دون تحليل الرؤوس بشكل متكرر. لدى Vulkan-HPP بعض الرؤوس الطويلة للغاية (على سبيل المثال vulkan_structs.hpp ) ، ومن المرجح أن تقصر وحدة C ++ أوقات التجميع للمشاريع التي تستخدمها حاليًا.
تتطلب هذه الميزة مترجمًا حديثًا مع دعم C ++ 20 الكامل:
cl.exe 19.28 أو أحدث)إذا كنت تنوي استخدام دعم وحدة C ++ Cmake (وربما النينجا) ، فستكون هناك حاجة إلى أدوات أكثر حداثة:
cl.exe 19.34 أو أحدث)تحذير
لا تزال الوحدة المسمى Vulkan-HPP C ++ تجريبية. 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 . على سبيل المثال:
# 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.
مرخصة بموجب ترخيص Apache ، الإصدار 2.0 ("الترخيص") ؛ لا يجوز لك استخدام هذا الملف إلا في الامتثال للترخيص. يمكنك الحصول على نسخة من الترخيص على
http://www.apache.org/licenses/LICENSE-2.0
ما لم يكن مطلوبًا بموجب القانون المعمول به أو الموافقة على الكتابة ، يتم توزيع البرامج الموزعة بموجب الترخيص على أساس "كما هي" ، دون ضمانات أو شروط من أي نوع ، إما صريحة أو ضمنية. راجع ترخيص الأذونات والقيود التي تحكم اللغة المحددة بموجب الترخيص.