はじめに
Vulkanを段階的に, 私のために学習していきます.私のための学習ですので, プログラミング, CGの基礎, 概要は省略します.
始めにライブラリのロードを行いました.
IntelのチュートリアルAPI without Secretsを参考にしています.
コードはGitHubに置くことにします.
ライブラリロード
VulkanのAPIはOpenGLと同様に, APIを動的リンクライブラリからロードして使用するスタイルです.Khronos GroupのVulkan Documentsに, API仕様がXMLファイルとして定義されています.
実装は各ベンダが提供します. C関数宣言としてのインターフェイスもXML仕様から生成されます.
とりあえず, Windows環境でデバイスの初期化を試してみます.
Vulkan (Ver. 1)のライブラリ名は"vulkan-1.dll"や"libvulkan.so.1"です.
"LoadLibrary"や"dlopen"でライブラリをロードし,
"GetProcAddress"や"dlsym"で関数アドレス取得,
"FreeLibrary"や"dlclose"でライブラリをアンロード, という流れになります.
#ifdef _WIN32 #define VK_USE_PLATFORM_WIN32_KHR 1 #define VLK_DLHANDLE HMODULE #define VLK_DLOPEN(path) LoadLibrary((path)) #define VLK_DLSYM(handle, name) GetProcAddress((handle), (name)) #define VLK_DLCLOSE(handle) FreeLibrary((handle)) #define VLK_VULKANLIB ("vulkan-1.dll") #else #define VK_USE_PLATFORM_XLIB_KHR 1 #define VLK_DLHANDLE void* #define VLK_DLOPEN(path) dlopen((path), RTLD_NOW) #define VLK_DLSYM(handle, name) dlsym((handle), (name)) #define VLK_DLCLOSE(handle) dlclose((handle)) #define VLK_VULKANLIB ("libvulkan.so.1") #endif namespace vk { //-------------------------------------------------------------- //--- //--- Lib //--- //-------------------------------------------------------------- class Lib { public: Lib(); ~Lib(); inline bool valid() const; VkResult initialize(); void terminate(); private: Lib(const Lib&) = delete; Lib& operator=(const Lib&) = delete; VLK_DLHANDLE handle_; }; inline bool Lib::valid() const { return VLK_NULL != handle_; } Lib::Lib() :handle_(VLK_NULL) { } Lib::~Lib() { terminate(); } VkResult Lib::initialize() { if(VLK_NULL != handle_){ return VK_ERROR_INITIALIZATION_FAILED; } handle_ = VLK_DLOPEN(VLK_VULKANLIB); if(VLK_NULL == handle_){ return VK_ERROR_INITIALIZATION_FAILED; } return VK_SUCCESS; } void Lib::terminate() { if(VLK_NULL != handle_){ VLK_DLCLOSE(handle_); handle_ = VLK_NULL; } } }
インスタンス, デバイス生成
次に関数ポインタをロードします.関数のプロトタイプは, Vulkan Documentsにヘッダファイルとして宣言があります.
PFN_(name)の型で関数ポインタの型宣言があるため, 例えば以下のように文字列でアドレスを取得します.
#define VLK_GET_FUNCTION(name) name ## _ = (PFN_ ## name)VLK_DLSYM(handle_, #name);APIが変更されることはほとんどないため, 手で全て書いてもよいですが面倒なので, vk.xmlから抽出します.
例えば, "VLK_EXPORTED_FUNCTION(vkCreateInstance)"として"VkFunctions.inc"に出力しておけば,
#define VLK_EXPORTED_FUNCTION(name) extern PFN_ ## name name ## _; #include "VkFunctions.inc" #define VLK_EXPORTED_FUNCTION(name) PFN_ ## name name ## _ = VLK_NULL; #include "VkFunctions.inc VkResult Lib::initialize() { if(VLK_NULL != handle_){ return VK_ERROR_INITIALIZATION_FAILED; } handle_ = VLK_DLOPEN(VLK_VULKANLIB); if(VLK_NULL == handle_){ return VK_ERROR_INITIALIZATION_FAILED; } #define VLK_EXPORTED_FUNCTION(name) VLK_GET_FUNCTION(name, "Error: cannot get exported %s\n") #define VLK_GET_FUNCTION(name, message) name ## _ = (PFN_ ## name)VLK_DLSYM(handle_, #name);\ if(VLK_NULL == name ## _){\ PRINT1_ERR(message, #name);\ return VK_ERROR_INITIALIZATION_FAILED;\ } #include "VkFunctions.inc" #undef VLK_GET_FUNCTION return VK_SUCCESS; }のようにしておくと, "(name)_"でグローバル関数として呼び出すことができます.
ここまでできると, 後は仕様どおりに初期化して使用するだけです.
とりあえず, デバイスの生成までできました.
クラスでwrapしていますが, vkCreateInstanceやvkCreateDeviceを呼び出しているだけです.
bool selectQueueFamily(vk::PhysicalDevice& selectedDevice, vk::u32& selectedQueueFamilty, vk::Instance& instance); int main(int /*argc*/, char** /*argv*/) { //Load library vk::Lib lib; if(VK_SUCCESS != lib.initialize()){ fprintf(stderr, "Fail to initialize lib\n"); return 0; } vk::Instance instance; vk::Device device; { // Create instance //--------------------------------------------------------------------- VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO, //structure type VLK_NULL, "Tutorial Vulkan", //application name VK_MAKE_VERSION(1,0,0), //application version "Tutorial Engine", //engine name VK_MAKE_VERSION(1,0,0), //engine version VK_API_VERSION_1_0, //api version }; VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, //structure type VLK_NULL, 0, //instance creation flags &applicationInfo, //application info 0, //enabled layer count VLK_NULL, //enabled layer names 0, //enabled extension count VLK_NULL, //enabled extension names }; if(VK_SUCCESS != lib.createInstance(instance, &instanceCreateInfo, VLK_NULL)){ fprintf(stderr, "Fail to create instance\n"); return 0; } // Create Device //--------------------------------------------------------------------- vk::PhysicalDevice physicalDevice; vk::u32 queueFamily; if(!selectQueueFamily(physicalDevice, queueFamily, instance)){ fprintf(stderr, "Fail to find queue family\n"); return 0; } const float queuePriorities[] = {0.0f}; VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //structure type VLK_NULL, 0, //device queue creation flags queueFamily, //selected queue family's index 1, //queue count queuePriorities, //queue priorities }; VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, //structure type VLK_NULL, 0, //device creation flags 1, //queue create info count, &queueCreateInfo, //queue create info 0, //enabled layer count VLK_NULL, //enabled layer names 0, //enabled extension count VLK_NULL, //enabled extension names VLK_NULL, //enabled physical device features }; if(VK_SUCCESS != physicalDevice.createDevice(device, &deviceCreateInfo, VLK_NULL)){ fprintf(stderr, "Fail to create device\n"); return 0; } } device.destroy(); instance.destroy(); lib.terminate(); return 0; } bool selectQueueFamily(vk::PhysicalDevice& selectedDevice, vk::u32& selectedQueueFamilty, vk::Instance& instance) { static const vk::u32 MaxProperties = 128; VkQueueFamilyProperties properties[MaxProperties]; vk::PhysicalDevices physicalDevices = instance.enumeratePhysicalDevices(); for(vk::u32 i=0; i<physicalDevices.getNumDevices(); ++i){ vk::PhysicalDevice& physicalDevice = physicalDevices.getDevice(i); vk::u32 countProperties = 0; physicalDevice.getPhysicalDeviceQueueFamilyProperties(&countProperties, VLK_NULL); countProperties = vk::minimum(countProperties, MaxProperties); physicalDevice.getPhysicalDeviceQueueFamilyProperties(&countProperties, properties); for(vk::u32 j=0; j<countProperties; ++j){ if(0<properties[j].queueCount && (properties[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { selectedDevice = physicalDevice; selectedQueueFamilty = j; return true; } } } return false; }
まとめ
vk.xmlから関数名を抽出し, デバイスの生成まで行いました.APIの基本設計はOpenGLと変わらないように思います.
XMLで関数仕様が記述されているため, ヘッダから抽出するより楽になったと思います.
0 件のコメント:
コメントを投稿